Уязвимости от mysql_escape_string

В последнее время я объяснял параметризацию и ее преимущества моему другу, и он спросил, как это лучше, чем mysqli_escape_string с точки зрения безопасности. В частности, можете ли вы придумать какие-либо примеры SQL-инъекций, которые бы преуспели, несмотря на то, что строки ввода были экранированы (используя mysqli_escape_string)?

ОБНОВИТЬ:

Приношу свои извинения за то, что я недостаточно ясно из моего первоначального вопроса. Общий вопрос, задаваемый здесь, – это возможная инъекция SQL, несмотря на то, что вы избегаете входных строк?

Обновленный ответ

Вопрос был отредактирован (после того, как мой ответ был опубликован) специально предназначался для mysqli_escape_string , который является псевдонимом mysql_real_escape_string и поэтому учитывает кодировку соединения. Это делает первоначальный ответ неприменимым, но я оставил его для полноты.

Короче говоря, новый ответ: mysqli_escape_string настолько же mysqli_escape_string как и параметризованные запросы, если вы не стреляете в ногу .

В частности, то, что вы не должны делать, выделяется в гигантском предупреждении на странице документа PHP:

Набор символов должен быть установлен либо на уровне сервера, либо с помощью функции API mysqli_set_charset() чтобы повлиять на mysqli_real_escape_string() .

Если вы не учтете это предупреждение (т. Е. Если вы измените набор символов с помощью прямого запроса SET NAMES ), и вы измените набор символов из однобайтовой кодировки на «удобную» (с точки зрения злоумышленника) многобайтовую кодировку, вы в действительности эмулирует то, что делает тупая mysql_escape_string : пытайтесь избежать символов, не зная, в какую кодировку вводится вход.

Эта ситуация оставляет вас потенциально уязвимыми для SQL-инъекций, как описано в первоначальном ответе ниже.

Важное замечание. Я помню, как я читал где-то, что последние версии MySql подключили это отверстие на их конце (в клиентских библиотеках?), Что означает, что вы можете быть абсолютно безопасны, даже если использовать SET NAMES для переключения на уязвимую многобайтовую кодировку. Но, пожалуйста, не забудь об этом.

Оригинальный ответ

В отличие от mysql_real_escape_string , голый mysql_escape_string не учитывает кодировку соединения . Это означает, что он предполагает, что входной сигнал находится в однобайтовой кодировке, тогда как на самом деле он может быть законным в многобайтовой кодировке.

Некоторые многобайтовые кодировки имеют байтовые последовательности, которые соответствуют одному символу, где один из байтов является значением ASCII одинарной кавычки ( 0x27 ); если кормить такую ​​строку, mysql_escape_string будет счастливо «избегать цитаты», что означает замену 0x27 на 0x5c 0x27 . В зависимости от правил кодирования это может привести к мутации многобайтового символа в другой, который включает в себя 0x5c и оставление «оставшегося» 0x27 в качестве отдельной одиночной кавычки на входе . Voilà, вы внесли небезупречную цитату в SQL.

Подробнее см. В этом сообщении в блоге .

Плохой ответ:

В частности, можете ли вы придумать какие-либо примеры SQL-инъекций, которые бы преуспели, несмотря на то, что строки ввода были экранированы (используя mysql_escape_string)?

Не надежно. Вы имеете в виду mysql_escape_string() , который не учитывает кодировку соединения (в то время как mysql_real_escape_string() ).

Поэтому, возможно, тщательно обработанная строка с тщательно обработанным незавершенным кодовым номером UTF8 впереди может привести, например, к знаку цитаты, который mysql_escape_string() но сам побег игнорируется MySQL, поскольку он «увидит» его как UTF8 персонаж.

Например:

 0xC2' OR 1=1 ;-- 

mysql_escape_string() будет экранирован как

 0xC2\' OR 1=1 ;-- 

которые будут собраны для

 WHERE password='0xC2\' OR 1=1 ;--'; 

и видели MySQL (если правильная кодировка соединения была в силе), как, скажем,

 WHERE password='€' OR 1=1 ;[--';] <-- the bracketed part is considered a comment and ignored 

которая будет классической инъекцией SQL.

Но это зависит от того, что вы указали, возможно, через отвлечение, дважды устаревшую функцию . Если вы действительно ссылались на mysql_real_escape_string() , то это не сработало.

Кроме того, это предполагает, что ни сервер, ни прикладной уровень (например, PHP) не используют проверку валидации набора символов при заполнении ввода. Если они это сделают, недопустимый UTF8 будет удален по прибытии и никогда не будет замечен mysql_escape_string , что тогда, конечно, было бы достаточно.

Настоящий ответ:

Не используйте mysql_escape_string (или mysql_whatever ) вообще. Они устарели, и ваш код может перестать работать. Вместо этого используйте функции PDO.