SQL инъекции и как от них защититься

« Предыдущая запись
 
  Следующая запись »
 

SQLinjection SQL инъекции и как от них защититься Вот и весна пришла, настоящая. На улице уже давно плюс, всё почти растаяло, хорошо, свежо… А значит на носу сессии и сдачи проектов для освобождения от головных болей летом. Вот и я во всё это погружен, хотя нашёл время чиркнуть пару строк.

Сегодня я затрону давно обещанную тему про SQL инъекции (не прошло и полгода… или прошло… – прим.ред.). Я постараюсь описать их как можно проще и покажу основный приёмы от их защиты (если конечно то, что я опишу, можно охарактеризовать как приёмы).

Прежде, чем начать, я хотел бы сослаться на википедию. Там выложена довольно неплохая статья про SQl injection, с которой стоит ознакомиться. В своей статье я опишу всё немного иначе, затрону то, чего там нет, но скорее всего в чём-то и повторюсь, но думаю в этом ничего криминального нет. Итак…

SQL инъекции очень распространены, чем-то похожи на XSS-атаки (подробнее в статье "Как защититься от XSS атаки и устранить уязвимость"), и основаны они на внедрении произвольного SQL-кода в какой-либо запрос (даже совсем безобидный), в результате чего злоумышленник получает из базы данных необходимую ему секретную информацию (ну, или просто удаляет всё к едрёной бабушке – прим.ред.).

Рассмотрим простой пример, чтобы показать как работают инъекции. Примитивный запрос формы авторизации – пользователь вводит логин и пароль, а далее идёт сравнение введённых данных с теми, что есть в базе данных.

SELECT * FROM users WHERE login = "$_POST['login']" AND passw = "$_POST['passw']"

В случае, когда переданные параметры не проверяются, можно творить, что угодно. К примеру, вместо логина можно ввести admin"/*, а в качестве пароля что угодно (иди не вводить ничего), тогда запрос будет выглядеть так:

SELECT * FROM users WHERE login = "admin"/* AND passw = ""

И такой запрос пройдёт как валидный и пользователь зайдёт под ником 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']);

Текстовые строки

С текстовыми строками не всё так просто. Конечно, если нужны только буквы и цифры, то все остальные символы можно спокойно отрубить регэкспом:

$string = preg_replace ("/[^a-zA-Z0-9]/i", "", $_POST['string']);

Но иногда всякие скобочки, галочки бывают реально нужны. Что делать?

Есть некоторые символы, которые ни в коем случае нельзя разрешать. По крайней мере напрямую, ведь всегда можно пробежаться регуляркой по тексту и заменить левые символы на спец.символы. Поговорим о вредных символах.

  1. Одинарные и двойные кавычки. Они используются в запросах для выделения. Думаю ещё из примера выше ясно, почему стоит их запрещать. В любом случае с ними заморачиваться особо и не надо, всегда есть htmlspecialchars и addslashes.
  2. Знак равенства. Казалось бы обычный безобидный значок, но и он может напакостить. К примеру, имеем запрос:
    $sql = 'SELECT * FROM users WHERE id='.$id;

    Передадим в качестве параметра  1 OR name="admin". В результате запрос будет выглядеть как

    SELECT * FROM users WHERE id=1 OR name="admin"

    Думаю понятно, что если запретить символ равенства, то вместо name="admin" будет name"admin", а это приведёт к ошибке и запрос не пройдёт.

  3. Символы комментариев. К символам комментариев относятся двойное тире и слэш со звёздочкой (его показывали в самом первом примере). Тут всё почти как с комментированием в PHP, то есть вместо двойного слэша используется двойное тире и комментит только одну строку, а слэш со звёздочкой точно также обозначен, только в SQL комментирует до конца запроса и его можно не закрывать. К примеру, есть запрос:
    SELECT * FROM users
    WHERE name=$id
      AND password=$pass

    Если мы внедрим вместо $id значение 1-- то последняя строчка с паролем не закомментируется и инъекция не прокатит. Но всё будет иначе, если вместо двойного тире использовать /*, тогда последняя строчка закомментируется.

 

Помимо вытягивания и удаления информации описанными выше способами, можно спокойно слить всю информацию и базы в файл или произвести дефейс сайта. И выглядит это так

SELECT * INTO OUTFILE 'file.php'

Таким образом можно создать собственный сценарий на сайте и уже плясать от него. Да и если файлы сценариев доступны для записи всем, то можно произвести дефейс:

SELECT '<b>YOU HACKED!</b>' INTO OUTFILE 'index.php'

 

На сегодня всё. Берегите свои скрипты от всякой нечисти. Удачи!


1 звезда2 звезд3 звезд4 звезд5 звезд (голосов: 4, средний: 5.00 из 5)
Понравилась статья или журнал? Подписывайся на продолжение!
Отзывов: 12 на запись

"SQL инъекции и как от них защититься"

  1. Люблю статьи на такие темы, но тут как то слабо ты расписал.. Жаль своего блога не веду, расписал бы несколько методов защиты, а так уже есть два рабочих метода которые пришлось писать из-за некоторых любопытных людей, поделюсь одним из них.

    Код функции:

    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();

  2. Смотря как посмотреть – я дал основы и руководство к действию, но не дал полностью готовых примеров.
    Посмотрел код, мне понравились проверки, особенно на директории выше уровнем (../), я про них даже как-то не подумал, что тоже стоит.
    Мне, кстати, в целом понравилось что сделано в плагине для WP anti-xss-attack, которая эту проверку вешает на админку. Может она не самая лучшая, но с задачей справляется. Основной смысл в сравнении хоста из HTTP_REFERER с текущим хостом.

  3. Кейтен
    08/05/2011 at 17:32 Постоянная ссылка Цитировать

    На самом деле есть реально проще способ избавиться от инъекций: шифруем все данные в base64 на входе и дешифруем на выходе. Это чтоб от всяких галочек там избавиться…
    Вообще с XSS сталкивался, а вот с SQL – нет…

  4. хм, не слышал про такой вариант. только не очень понимаю, он вроде бы только теги обрезает при кодировании, а всякие кавычки и слэши остаются, разве нет?

  5. Руслан
    09/05/2011 at 17:42 Постоянная ссылка Цитировать

    Про кодирование в base64 я давно знаю, это уже пройденный этап.. Этим уже давно не защитишься… Я уже более 3х лет занимаюсь разработкой защиты для своей CMS, и каждый день я узнаю что-то новое..

  6. Поделитесь ещё какой-нибудь фишкой, неупомянутой выше, на благо читателей и владельца блога? =)

  7. Руслан
    09/05/2011 at 18:41 Постоянная ссылка Цитировать

    Поближе к выходным наберусь сил) и распишу что-нибудь подробнее)

  8. Спасибо, будем ждать!)

  9. На счет запроса:
    SELECT * FROM users WHERE id=1; DELETE FROM users
    Не прокатит такая конструкция, запросы разделяются таким символом ";" в phpmyadmin, на живых же системах, никто не эксплодит запросы по точке с запятой, что бы потом их в цикле выполнить, иначе как они будут работать с результатами запроса?... сама консnрукция mysql_query не поддерживает такой вариант.

  10. Приношу свои извинения за долгий ответ – только сейчас случайно увидел, что есть комментарий без ответа.
    Хммм, для меня это явилось некоторым откровением, видимо неправильно мне давали матчасть. Большое спасибо за указанную ошибку.
    Может быть я перепутал какое-то другое “назначение” точки с запятой? Или это дефектная матчасть всё таки была…

  11. я юзаю mysql_real_escape_string(), экранирует кавычки и прочую нечисть обратными слешами и норма :)

  12. ну да, совместно используя с get_magic_quotes_gpc()

Добро пожаловать, коллега! Вы можете оставить свой отзыв:





Допустимые XHTML-теги:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Подписка на комментарии