Вот и весна пришла, настоящая. На улице уже давно плюс, всё почти растаяло, хорошо, свежо… А значит на носу сессии и сдачи проектов для освобождения от головных болей летом. Вот и я во всё это погружен, хотя нашёл время чиркнуть пару строк.
Сегодня я затрону давно обещанную тему про SQL инъекции (не прошло и полгода… или прошло… – прим.ред.). Я постараюсь описать их как можно проще и покажу основный приёмы от их защиты (если конечно то, что я опишу, можно охарактеризовать как приёмы).
Прежде, чем начать, я хотел бы сослаться на википедию. Там выложена довольно неплохая статья про SQl injection, с которой стоит ознакомиться. В своей статье я опишу всё немного иначе, затрону то, чего там нет, но скорее всего в чём-то и повторюсь, но думаю в этом ничего криминального нет. Итак…
SQL инъекции очень распространены, чем-то похожи на XSS-атаки (подробнее в статье "Как защититься от XSS атаки и устранить уязвимость"), и основаны они на внедрении произвольного SQL-кода в какой-либо запрос (даже совсем безобидный), в результате чего злоумышленник получает из базы данных необходимую ему секретную информацию (ну, или просто удаляет всё к едрёной бабушке – прим.ред.).
Рассмотрим простой пример, чтобы показать как работают инъекции. Примитивный запрос формы авторизации – пользователь вводит логин и пароль, а далее идёт сравнение введённых данных с теми, что есть в базе данных.
В случае, когда переданные параметры не проверяются, можно творить, что угодно. К примеру, вместо логина можно ввести admin"/*, а в качестве пароля что угодно (иди не вводить ничего), тогда запрос будет выглядеть так:
И такой запрос пройдёт как валидный и пользователь зайдёт под ником admin. Почему? /* – является началом комментария, и, в отличие от PHP, его необязательно закрывать. Вот и всё.
Отсюда лезет вывод – необходимо проверять введённые данные, в первую очередь на уровне PHP. На уровне JavaScript тоже неплохо проверять, но только как дополнение к проверке на PHP. Возьмём тот же пример с проверками на JavaScript. Вроде бы ничего ввести не можем, НО. К примеру, у нас включён register_globals (подробнее в статье "Почему опасно включать параметр register_globals"), тогда в адресную строку нужно лишь тупо дописать ?login=admin"/* и всё, результат тот же самый.
Защита от SQL injection
Числовые строки
Начну с простого, с числовой строки. Если мы передаём число, то чтобы в него ничего не внедрили, необходимо, чтобы все символы, кроме числовых, были выкинуты. Два способа:
-
Показываем сценарию, что переданное значение именно число.$number = (int)$_POST['number'];
Минус этого метода (или не минус, кому как) следующий – если передаётся не число, то в переменную $number попадёт нолик. Страшного тут ничего нет, но только в случае, если есть какая-то секретная нулевая запись. Впрочем, сценарий всегда можно переписать, проверив, является ли значение нулём.
-
Замена по регулярному выражению, где обрубается всё, кроме цифр.$number = preg_replace ("/[^0-9]/", "", $_POST['number']);
Текстовые строки
С текстовыми строками не всё так просто. Конечно, если нужны только буквы и цифры, то все остальные символы можно спокойно отрубить регэкспом:
Но иногда всякие скобочки, галочки бывают реально нужны. Что делать?
Есть некоторые символы, которые ни в коем случае нельзя разрешать. По крайней мере напрямую, ведь всегда можно пробежаться регуляркой по тексту и заменить левые символы на спец.символы. Поговорим о вредных символах.
-
Одинарные и двойные кавычки. Они используются в запросах для выделения. Думаю ещё из примера выше ясно, почему стоит их запрещать. В любом случае с ними заморачиваться особо и не надо, всегда есть htmlspecialchars и addslashes.
-
Знак равенства. Казалось бы обычный безобидный значок, но и он может напакостить. К примеру, имеем запрос:$sql = 'SELECT * FROM users WHERE id='.$id;
Передадим в качестве параметра 1 OR name="admin". В результате запрос будет выглядеть как
SELECT * FROM users WHERE id=1 OR name="admin"Думаю понятно, что если запретить символ равенства, то вместо name="admin" будет name"admin", а это приведёт к ошибке и запрос не пройдёт.
-
Символы комментариев. К символам комментариев относятся двойное тире и слэш со звёздочкой (его показывали в самом первом примере). Тут всё почти как с комментированием в PHP, то есть вместо двойного слэша используется двойное тире и комментит только одну строку, а слэш со звёздочкой точно также обозначен, только в SQL комментирует до конца запроса и его можно не закрывать. К примеру, есть запрос:SELECT * FROM users
WHERE name=$id
AND password=$passЕсли мы внедрим вместо $id значение 1-- то последняя строчка с паролем не закомментируется и инъекция не прокатит. Но всё будет иначе, если вместо двойного тире использовать /*, тогда последняя строчка закомментируется.
Помимо вытягивания и удаления информации описанными выше способами, можно спокойно слить всю информацию и базы в файл или произвести дефейс сайта. И выглядит это так
Таким образом можно создать собственный сценарий на сайте и уже плясать от него. Да и если файлы сценариев доступны для записи всем, то можно произвести дефейс:
На сегодня всё. Берегите свои скрипты от всякой нечисти. Удачи!
30/04/2011 at 5:35 Постоянная ссылка Цитировать
Люблю статьи на такие темы, но тут как то слабо ты расписал.. Жаль своего блога не веду, расписал бы несколько методов защиты, а так уже есть два рабочих метода которые пришлось писать из-за некоторых любопытных людей, поделюсь одним из них.
Код функции:
function check_xss() {
$url = html_entity_decode( urldecode( $_SERVER['QUERY_STRING'] ) );
$url = str_replace( "\\", "/", $url );
if( $url ) {
if( (strpos( $url, '<' ) !== false) || (strpos( $url, '>' ) !== false) || (strpos( $url, '"' ) !== false) || (strpos( $url, './' ) !== false) || (strpos( $url, '../' ) !== false) || (strpos( $url, '\'' ) !== false)) {
if( $_GET['mod'] != "editnews" or $_GET['action'] != "list" ) die( "Привет, как твои дела? Ничего не получается?:(" );
}
}
$url = html_entity_decode( urldecode( $_SERVER['REQUEST_URI'] ) );
$url = str_replace( "\\", "/", $url );
if( $url ) {
if( (strpos( $url, '<' ) !== false) || (strpos( $url, '>' ) !== false) || (strpos( $url, '"' ) !== false) || (strpos( $url, '\'' ) !== false) ) {
die( "Привет, как твои дела? Ничего не получается?:(" );
}
}
}
И подключаем саму функцию:
check_xss();
30/04/2011 at 6:31 Постоянная ссылка Цитировать
Смотря как посмотреть – я дал основы и руководство к действию, но не дал полностью готовых примеров.
Посмотрел код, мне понравились проверки, особенно на директории выше уровнем (../), я про них даже как-то не подумал, что тоже стоит.
Мне, кстати, в целом понравилось что сделано в плагине для WP anti-xss-attack, которая эту проверку вешает на админку. Может она не самая лучшая, но с задачей справляется. Основной смысл в сравнении хоста из HTTP_REFERER с текущим хостом.
08/05/2011 at 17:32 Постоянная ссылка Цитировать
На самом деле есть реально проще способ избавиться от инъекций: шифруем все данные в base64 на входе и дешифруем на выходе. Это чтоб от всяких галочек там избавиться…
Вообще с XSS сталкивался, а вот с SQL – нет…
09/05/2011 at 6:36 Постоянная ссылка Цитировать
хм, не слышал про такой вариант. только не очень понимаю, он вроде бы только теги обрезает при кодировании, а всякие кавычки и слэши остаются, разве нет?
09/05/2011 at 17:42 Постоянная ссылка Цитировать
Про кодирование в base64 я давно знаю, это уже пройденный этап.. Этим уже давно не защитишься… Я уже более 3х лет занимаюсь разработкой защиты для своей CMS, и каждый день я узнаю что-то новое..
09/05/2011 at 17:57 Постоянная ссылка Цитировать
Поделитесь ещё какой-нибудь фишкой, неупомянутой выше, на благо читателей и владельца блога? =)
09/05/2011 at 18:41 Постоянная ссылка Цитировать
Поближе к выходным наберусь сил) и распишу что-нибудь подробнее)
09/05/2011 at 18:55 Постоянная ссылка Цитировать
Спасибо, будем ждать!)
24/10/2011 at 14:03 Постоянная ссылка Цитировать
На счет запроса:
SELECT * FROM users WHERE id=1; DELETE FROM users
Не прокатит такая конструкция, запросы разделяются таким символом ";" в phpmyadmin, на живых же системах, никто не эксплодит запросы по точке с запятой, что бы потом их в цикле выполнить, иначе как они будут работать с результатами запроса?... сама консnрукция mysql_query не поддерживает такой вариант.
10/01/2012 at 16:03 Постоянная ссылка Цитировать
Приношу свои извинения за долгий ответ – только сейчас случайно увидел, что есть комментарий без ответа.
Хммм, для меня это явилось некоторым откровением, видимо неправильно мне давали матчасть. Большое спасибо за указанную ошибку.
Может быть я перепутал какое-то другое “назначение” точки с запятой? Или это дефектная матчасть всё таки была…
22/01/2012 at 22:05 Постоянная ссылка Цитировать
я юзаю mysql_real_escape_string(), экранирует кавычки и прочую нечисть обратными слешами и норма
22/01/2012 at 23:59 Постоянная ссылка Цитировать
ну да, совместно используя с get_magic_quotes_gpc()