SQL-инъекция, обхватывающая mysql_real_escape_string()
Подскажите, возможна ли уязвимость для SQL-инъекции даже с использованием функции mysql_real_escape_string()
?
Рассмотрим следующую ситуацию. SQL-код формируется в PHP так:
$login = mysql_real_escape_string(GetFromPost('login'));
$password = mysql_real_escape_string(GetFromPost('password'));
$sql = "SELECT * FROM table WHERE login='$login' AND password='$password'";
Я слышал много раз, что такой код все еще опасен и его можно взломать, даже при использовании функции mysql_real_escape_string()
. Но я не могу представить себе никакого возможного способа эксплуатации этой уязвимости.
Классические инъекции, такие как:
aaa' OR 1=1 --
не работают.
Знаете ли вы о каких-либо возможных инъекциях, которые могли бы обойти приведенный выше PHP-код?
2 ответ(ов)
Используемый вами код может вызывать опасения по поводу уязвимости SQL-инъекций, несмотря на то, что вы используете mysql_real_escape_string()
. Давайте разберемся, почему это так.
Когда вы используете mysql_real_escape_string()
, вы, конечно, экранируете специальные символы, чтобы защитить себя от инъекций, но это не сработает, если вы передаете строку, содержащую SQL-команды, как в вашем примере:
$iId = mysql_real_escape_string("1 OR 1=1");
$sSql = "SELECT * FROM table WHERE id = $iId";
В данном случае, результатом работы mysql_real_escape_string()
будет строка, которая не изменит суть SQL-запроса, поскольку часть OR 1=1
всё равно будет интерпретироваться как часть SQL-запроса. Поэтому данная защита не является достаточной.
Ключевой момент заключается в том, что использование одинарных кавычек (' '
) вокруг переменных в SQL-запросе может обеспечить защиту от этого. Например, если вы обернете идентификатор в одинарные кавычки:
$iId = mysql_real_escape_string("1 OR 1=1");
$sSql = "SELECT * FROM table WHERE id = '$iId'";
Таким образом, id
будет обрабатываться как строка, и условие OR 1=1
не сработает.
Также вы можете использовать приведение типов для предотвращения инъекций, как в следующем примере:
$iId = (int)"1 OR 1=1";
$sSql = "SELECT * FROM table WHERE id = $iId";
Такой подход является более безопасным, так как преобразование строкового значения, содержащего SQL-код, в целое число (интегер) автоматически удалит все нечисловые символы. В результате в приведённом коде 1 OR 1=1
будет преобразовано в 1
, и злоумышленник не сможет провести SQL-инъекцию.
Рекомендуется использовать параметры в подготовленных выражениях (prepared statements), если это возможно, для обеспечения максимальной безопасности при работе с базами данных.
На самом деле, ничего нельзя использовать для обхода этого, кроме символа %
как подстановочного знака. Это может быть опасно, если вы используете оператор LIKE
, так как злоумышленник может ввести просто %
в качестве логина, если вы это не отфильтровали, и затем ему останется только перебрать пароль любого из ваших пользователей.
Часто рекомендуют использовать подготовленные запросы, чтобы сделать это на 100% безопасно, так как данные не могут вмешиваться в саму SQL-запрос таким образом.
Однако для таких простых запросов, вероятно, будет более эффективно использовать что-то вроде $login = preg_replace('/[^a-zA-Z0-9_]/', '', $login);
, чтобы фильтровать вводимые данные.
Как предотвратить SQL-инъекции в PHP?
Как санировать пользовательский ввод с помощью PHP?
Достаточны ли подготовленные выражения PDO для предотвращения SQL-инъекций?
SQL: выбрать только строки с максимальным значением в столбце
Когда использовать одинарные кавычки, двойные кавычки и обратные кавычки в MySQL