SQL инъекции. Проверка, взлом, защита
14 октября 2011
SQL инъекция - это один из самых доступных способов взлома сайта.
Суть таких инъекций – внедрение в данные (передаваемые через GET, POST запросы или значения Cookie) произвольного SQL кода. Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно.
Изменяем GET запрос на ?detail=1' или ?detail=1". Далее пробуем передавать эти запросы серверу, т.е заходим на http://test.ru/?detail=1' или на http://test.ru/?detail=1".
Если при заходе на данные страницы появляется ошибка, значит сайт уязвим на SQL инъекции.
Пример ошибки, возникающей при проверке уязвимости

2) Присоединение к запросу результатов другого запроса. Делается это через оператор UNION.
3) Закомментирование части запроса.
Список новостей, разрешённых к публикации

При обращении к странице http://test.ru/?detail=4, которая должна выводить четвёртую новость появляется ошибка – новость не найдена. В нашем случае новость существует, но она запрещена к публикации.

Но так как мы уже проверяли сайт на уязвимость и он выдавал ошибку БД, то пробуем перебирать возможные варианты запросов.
В адресной строке плюс (+) выполняет роль пробела, так что не пугайтесь
Тестирую следующие варианты:
http://test.ru/?detail=4+OR+1
http://test.ru/?detail=4+--
http://test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4
В итоге удача улыбнулась и два запроса (первый и третий) вернули нам детальное описание четвёртой новости

Мало того, что $detail_id получает значение без какой либо обработки, так ещё и конструкция `id`=$detail_id написана криво, лучше придерживаться `id`='$detail_id' (т.е сравниваемое значение писать в прямых апострофах).
Глядя на запрос, получаемый при обращении к странице через http://test.ru/?detail=4+OR+1
SELECT * FROM `news` WHERE `public`='1' AND `id`=4 OR 1 ORDER BY `position` DESC
становится не совсем ясно, почему отобразилась 4-ая новость. Дело в том, что запрос вернул все записи из таблицы новостей, отсортированные в порядке убывания сверху. И таким образом наша 4-ая новость оказалась самой первой, она же и вывелась как детальная. Т.е просто совпадение.
Разбираем запрос, сформированный при обращении через http://test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4. Тут название таблицы с новостями (в нашем случае это news) бралось логическим перебором.
Итак, выполнился запрос SELECT * FROM `news` WHERE `public`='1' AND `id`=4 UNION SELECT * FROM news WHERE id=4 ORDER BY `position` DESC. К нулевому результату первой части запроса (до UNION) присоединился результат второй части (после UNION), вернувшей детальное описание 4-ой новости.
ЧИСЛА
Для проверки переменной на числовое значение используется функция is_numeric(n);, которая вернёт true, если параметр n - число, и false в противном случае.
Так же можно не проверять значение на число, а вручную переопределить тип. Вот пример, переопределяющий значение $id, полученное от $_GET['id_news'] в значение целочисленного типа (в целое число):
СТРОКИ
Большинство взломов через SQL происходят по причине нахождения в строках «необезвреженных» кавычек, апострофов и других специальных символов. Для такого обезвреживания нужно использовать функцию addslashes($str);, которая возвращает строку $str с добавленным обратным слешем (\) перед каждым специальным символом. Данный процесс называется экранизацией.
Кроме этого существуют две функции, созданные именно для экранизации строк, используемых в SQL выражениях.
Это функция mysql_escape_string($str); и mysql_real_escape_string($str);.
Первая не учитывает кодировку соединения с БД и может быть обойдена, а вот вторая её учитывает и абсолютно безопасна. mysql_real_escape_string($str); возвращает строку $str с добавленным обратным слешем к следующим символам: \x00, \n, \r, \, ', " и \x1a.
Как-то раз на Хабре какой-то дебил сетовал, что параметр get_magic_quotes_gpc ушёл в прошлое и сейчас на хостингах он везде отключен. Доверившись таким знаниям, в одном из проектов я решил не делать проверку магических кавечек. В результате меня ждал неприятный сюрприз и понимание, что верить нужно только себе. Поэтому, не доверяйтесь на то, что может быть в теории, а всё-равно делайте проверку.
Проверка включённости магических кавычек для данных получаемых из GET, POST или Куков организуется через функцию get_magic_quotes_gpc(); (возвращает 1 – если магические кавычки включены, 0 – если отключены).
Если магические кавычки включены (т.е обратные слеши добавляются) и такое встречается чаще, то их нужно убрать. Это делается через функцию stripslashes($str); (возвращает строку $str без обратных слешей у кавычек и прямых апострофов).
В заключении привожу код с полной экранизацией строк для записи в БД
Суть таких инъекций – внедрение в данные (передаваемые через GET, POST запросы или значения Cookie) произвольного SQL кода. Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно.
Как вычислить уязвимость, позволяющую внедрять SQL инъекции?
Глупые огрехи в коде, позволяющие сломать страницу, вычислить довольно легко. Допустим, имеется тестовый сайт http://test.ru. На сайте выводится список новостей, с возможностью детального просмотра. Адрес страницы с детальным описанием новости выглядит так: http://test.ru/?detail=1. Т.е через GET запрос переменная detail передаёт значение 1 (которое является идентификатором записи в таблице новостей).Изменяем GET запрос на ?detail=1' или ?detail=1". Далее пробуем передавать эти запросы серверу, т.е заходим на http://test.ru/?detail=1' или на http://test.ru/?detail=1".
Если при заходе на данные страницы появляется ошибка, значит сайт уязвим на SQL инъекции.
Пример ошибки, возникающей при проверке уязвимости

Возможные SQL инъекции (SQL внедрения)
1) Наиболее простые - сворачивание условия WHERE к истинному результату при любых значениях параметров.2) Присоединение к запросу результатов другого запроса. Делается это через оператор UNION.
3) Закомментирование части запроса.
Практика. Варианты взлома сайта с уязвимостью на SQL внедрения
Итак, у нас есть уже упоминавшийся сайт http://test.ru. В базе хранится 4 новости, 3 из которых выводятся. Разрешение на публикацию новости зависит от параметра public (если параметр содержит значение 1, то новость публикуется).Список новостей, разрешённых к публикации

При обращении к странице http://test.ru/?detail=4, которая должна выводить четвёртую новость появляется ошибка – новость не найдена. В нашем случае новость существует, но она запрещена к публикации.

Но так как мы уже проверяли сайт на уязвимость и он выдавал ошибку БД, то пробуем перебирать возможные варианты запросов.
В адресной строке плюс (+) выполняет роль пробела, так что не пугайтесь
Тестирую следующие варианты:
http://test.ru/?detail=4+OR+1
http://test.ru/?detail=4+--
http://test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4
В итоге удача улыбнулась и два запроса (первый и третий) вернули нам детальное описание четвёртой новости

Разбор примера изнутри
За получение детального описания новости отвечает блок кода:$detail_id=$_GET['detail'];
$zapros="SELECT * FROM `$table_news` WHERE `public`='1' AND `id`=$detail_id ORDER BY `position` DESC";
Мало того, что $detail_id получает значение без какой либо обработки, так ещё и конструкция `id`=$detail_id написана криво, лучше придерживаться `id`='$detail_id' (т.е сравниваемое значение писать в прямых апострофах).
Глядя на запрос, получаемый при обращении к странице через http://test.ru/?detail=4+OR+1
SELECT * FROM `news` WHERE `public`='1' AND `id`=4 OR 1 ORDER BY `position` DESC
становится не совсем ясно, почему отобразилась 4-ая новость. Дело в том, что запрос вернул все записи из таблицы новостей, отсортированные в порядке убывания сверху. И таким образом наша 4-ая новость оказалась самой первой, она же и вывелась как детальная. Т.е просто совпадение.
Разбираем запрос, сформированный при обращении через http://test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4. Тут название таблицы с новостями (в нашем случае это news) бралось логическим перебором.
Итак, выполнился запрос SELECT * FROM `news` WHERE `public`='1' AND `id`=4 UNION SELECT * FROM news WHERE id=4 ORDER BY `position` DESC. К нулевому результату первой части запроса (до UNION) присоединился результат второй части (после UNION), вернувшей детальное описание 4-ой новости.
Защита от SQL инъекций (SQL внедрений)
Защита от взлома сводится к базовому правилу «доверяй, но проверяй». Проверять нужно всё – числа, строки, даты, данные в специальных форматах.ЧИСЛА
Для проверки переменной на числовое значение используется функция is_numeric(n);, которая вернёт true, если параметр n - число, и false в противном случае.
Так же можно не проверять значение на число, а вручную переопределить тип. Вот пример, переопределяющий значение $id, полученное от $_GET['id_news'] в значение целочисленного типа (в целое число):
$id=(int)$_GET['id_news'];
СТРОКИ
Большинство взломов через SQL происходят по причине нахождения в строках «необезвреженных» кавычек, апострофов и других специальных символов. Для такого обезвреживания нужно использовать функцию addslashes($str);, которая возвращает строку $str с добавленным обратным слешем (\) перед каждым специальным символом. Данный процесс называется экранизацией.
$a="пример текста с апострофом ' ";
echo addslashes($a); //будет выведено: пример текста с апострофом \'
Кроме этого существуют две функции, созданные именно для экранизации строк, используемых в SQL выражениях.
Это функция mysql_escape_string($str); и mysql_real_escape_string($str);.
Первая не учитывает кодировку соединения с БД и может быть обойдена, а вот вторая её учитывает и абсолютно безопасна. mysql_real_escape_string($str); возвращает строку $str с добавленным обратным слешем к следующим символам: \x00, \n, \r, \, ', " и \x1a.
Магические кавычки
Магические кавычки – функция автоматической замены кавычки на обратный слеш (\) и кавычку при операциях ввода/вывода. В некоторых конфигурациях PHP этот параметр включён, а в некоторых нет. Для того, что бы избежать двойного экранизирования символов и заэкранизировать данные по-нормальному через mysql_real_escape_string($str);, необходимо убрать автоматические проставленные обратные слеши (если магические кавычки включены).Как-то раз на Хабре какой-то дебил сетовал, что параметр get_magic_quotes_gpc ушёл в прошлое и сейчас на хостингах он везде отключен. Доверившись таким знаниям, в одном из проектов я решил не делать проверку магических кавечек. В результате меня ждал неприятный сюрприз и понимание, что верить нужно только себе. Поэтому, не доверяйтесь на то, что может быть в теории, а всё-равно делайте проверку.
Проверка включённости магических кавычек для данных получаемых из GET, POST или Куков организуется через функцию get_magic_quotes_gpc(); (возвращает 1 – если магические кавычки включены, 0 – если отключены).
Если магические кавычки включены (т.е обратные слеши добавляются) и такое встречается чаще, то их нужно убрать. Это делается через функцию stripslashes($str); (возвращает строку $str без обратных слешей у кавычек и прямых апострофов).
В заключении привожу код с полной экранизацией строк для записи в БД
if(get_magic_quotes_gpc()==1)
{
$element_title=stripslashes(trim($_POST["element_title"]));
$element_text=stripslashes(trim($_POST["element_text"]));
$element_date=stripslashes(trim($_POST["element_date"]));
}
else
{
$element_title=trim($_POST["element_title"]);
$element_text=trim($_POST["element_text"]);
$element_date=trim($_POST["element_date"]);
}
$element_title=mysql_real_escape_string($element_title);
$element_text=mysql_real_escape_string($element_text);
$element_date=mysql_real_escape_string($element_date);