Reputation: 21
I'm looking for a way to prevent SQL Injection without using prepared statements. I know prepared statements are the correct way to prevent SQL Injection, but MySQL doesn't allow parameters to be used in an ALTER USER prepared statement, so I need an alternative:
# Will not work
CREATE DEFINER=`root`@`localhost` PROCEDURE `TestProcedure`()
BEGIN
SET @InputVal = 'NewPassword';
SET @SQLStr = 'ALTER USER \'SomeUser\'@\'localhost\' IDENTIFIED BY ?';
PREPARE SQLStatement FROM @SQLStr;
EXECUTE SQLStatement USING @InputVal;
DEALLOCATE PREPARE SQLStatement;
END
The specific goal is to create a stored procedure that allows users to change their password. I have it working with the assumption that alphanumeric inputs cannot lead to SQL injection (presumably a fair enough assumption to make, but I'm open to correction since I've seen some very creative SQL Injections using things like backspace characters):
CREATE DEFINER=`root`@`localhost` PROCEDURE `ChangeCurrentUserPassword`(IN NewPassword VARCHAR(30))
BEGIN
SET @NewPassword = NewPassword;
IF @NewPassword REGEXP '^[a-z0-9]{1,}$' THEN
SET @SQLStr = CONCAT('ALTER USER \'', Substring_Index(USER(),'@',1), '\'@\'localhost\' IDENTIFIED BY \'', NewPassword, '\';');
PREPARE SQLStatement FROM @SQLStr;
EXECUTE SQLStatement;
DEALLOCATE PREPARE SQLStatement;
ELSE
# Non-alphanumeric (or blank) password.
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Password must be alpha-numeric';
END IF;
END
The above does work as-intended and is presumably secure (aside from the fact that it allows single-character passwords - it was just a throw-together to test the logic), but I don't like the fact that it limits passwords to alpha-numeric only. I would like to as many non-alpha-numeric characters as possible that I can add into the REGEXP statement.
I have seen a lot of other threads asking about using REGEXP to detect SQL injection, but for each of them they were asking about detecting from any raw input. The key difference here is that rather than trying to detect SQL Injection attempts, I'm trying to limit the allowable characters so that SQL injection is not possible in the first place.
Does anyone know of a list of characters that can be included but still safely prevents SQL Injection?
Side-note: I'm happy to go a whole new approach to this procedure if there is a better way that still allows a procedure. I know I can UPDATE the mysql.user table directly which will allow for prepared statements being used properly, but I'd like to avoid updating system tables directly - I feel statements like ALTER USER exist purely because direct updates should be avoided where possible.
Sorry this was long-winded. Thanks for taking the time to get to this point!
Upvotes: 2
Views: 392
Reputation: 44108
Read the answers to SQL injection that gets around mysql_real_escape_string(), especially this one.
This is an old answer that references deprecated PHP function mysql_real_escape_string
. This has been replaced by mysqli_real_escape_string
, which encodes the following special characters:
NUL (ASCII 0), \n, \r, \, ', ", and Control-Z
where \n
is the newline character (ASCII x'0A') and \r
is the "carriage return" character (ASCII x'0D') .
mysqli_set_charset($link, 'utf8');
$s = mysqli_real_escape_string($link, chr(0) . "\n\r\\'" . '"' . chr(26));
var_dump($s);
Prints:
string(14) "\0\n\r\\\'\"\Z"
So:
utf8mb4
or utf8
.So, for example, if you only allow alphanumerics [A-Za-z] plus special characters from '@#$%&!_', then no escaping would be required. If you additionally allowed single and or double quotes, then you just have to make sure that these are escaped with a backslash.
Upvotes: 1