в предыдущих статьях говорилось о php-инъекции и xss. здесь будет рассказано о не менее страшной уязвимости, называемой sql-инъекция, с помощью которой можно получить содержимое всех баз и таблиц, доступных текущему mysql-пользователю.
sql-инъекция
здесь дело также в недостаточной фильтрации, а иногда от недопонимания принципа работы sql.
$id = $_GET['id'];
$pass = $_GET['pass'];
$q = "SELECT * FROM `table` WHERE `id` = $id AND `pass` = '$pass'";
$id = addslashes($_GET['id']);
$q = "SELECT * FROM `table` WHERE `id` = $id";
вариант из первой строки просто кричит о sql-инъекции. в самом деле, подставив с адресной строке браузера такой запрос:
http://site.ru/?id=1+--+
можно запросто авторизоваться хоть под администратором: поле pass обрабатываться не будет, т. к. оно закомментировано (в sql 2 знака минус с последующим пробелом означают комментарий до конца строки).
но вот в 4 строке вроде бы данные обрабатываются. только происходит совсем не то, что думает программист. достаточно передать такой параметр, чтобы убедиться в баге скрипта:
http://site.ru/?id=0+UNION+SELECT+1,2,3,4
в зависимости от того, сколько полей в выборке, второй select легко дополнить/урезать нужным количеством чисел, разделённых запятой. т. е. может понадобится передать SELECT+1,2,3,4,5,6
. это в любом случае не проблема: пробежавшись по значениям, допустим, до 20, злоумышленник найдёт нужное. и после этого получит всё, что душе угодно. как? да элементарно. с 5 версии mysql содержит содержит специальную базу information_schema, в которой есть таблица TABLES. вот из неё-то и можно легко получить все значения баз данных/таблиц, доступных текущему пользователю.
вложенный select нужно передать в том параметре, который участвует в выводе на экран. какой это параметр узнали из предыдущего запроса (когда передавали 1,2,3,4; какая цифра в браузере отобразилась, заместо той вложенная выборка и ставится). и пусть идёт получение 1 параметра, ничего не стоит написать небольшой скрипт на php, в котором «зациклить» значения получаемых смещений:
LIMIT+0,1
LIMIT+1,1
LIMIT+2,1
и т. д., пока не закончатся базы.
теперь, имея имена баз, можно получить имена таблиц. если попробовать следующий запрос, ничего не получится:
здесь basename — имя любой из базы данных, доступное на предыдущем шаге. в данном случае функция addslashes сработала и защитила от кражи данных. неопытный ломатель на этом шаге может отступиться, но более опытный смекнёт в чём дело и будет использовать такой запрос:
входная строка не содержит кавычек, а значит функция заслэшивания сработает впустую, а злоумышленник получит имена всех таблиц каждой из базы.
потом сходным запросом из таблицы COLUMNS достаются имена полей каждой таблицы. ну а после этого вся информация с сайта аккуратно складируется на компьютере злоумышленника и тот может делать с ней что угодно.
так как же не допустить утечки информации? если в запросе ожидается целое значение, то проще поступившую переменную привести к данному типу. если ожидается строковое представление, то значение обязательно должно быть обработано специальной функцией, а в запросе — заключено в кавычки:
$id = (int) $_GET['id'];
$pass = mysql_real_escape_string($_GET['pass']);
$q = "SELECT * FROM `table` WHERE `id` = $id AND `pass` = '$pass'";
зачем нужна специальная функция, понятно из описаний:
addslashes — заслэшивает кавычку ‘, двойную кавычку " и NUL-символ;
mysql_real_escape_string — помимо кавычки, двойной кавычки и NUL экранированию подвергаются переводы строк: \r и \n, обратная косая черта \ и символ с кодом x1a.
функцию можно вызывать только после установления успешного соединения с базой данных MySQL. есть более короткий аналог — функция mysql_escape_string, но она работает независимо от кодировки передаваемых строк, поэтому для UTF-16, допустим, будет давать некорректные результаты. но справедливости ради можно отметить, что проектов, использующих данную кодировку, мне не встречалось.
а для обычного текста в однобайтной кодировке, или же многобайтной UTF-8 из символов с кодами x00–x1f нужны только \r (x0d), \n (x0a) и в некоторых случаях \t (x09) — всё остальное логично безжалостно вырезать.
1. несмотря на то, что код, выводимый в браузер пользователя, формируется скриптом, кто-то может подсмотреть адрес и составить свой запрос. нельзя доверять никаким данным, пришедшим от пользователя;
2. перед записью в базу все внешние данные, участвующие в запросе, должны быть соответствующим образом обработаны: целые/вещественные лучше явно привести к требуемому типу, строки следует обработать функцией mysql-экранирования и в запросе заключить в кавычки.
днём интернета
шоколадкой для работы мозга
коробочкой ароматного чая для бодрости
продлением хостинга на +1 месяц