SQL инъекции. Проверка, взлом, защита

SQL инъекция — это один из самых доступных способов взлома сайта.
Суть таких инъекций – внедрение в данные (передаваемые через GET, POST запросы или значения Cookie) произвольного SQL кода. Если сайт уязвим и выполняет такие инъекции, то по сути есть возможность творить с БД (чаще всего это MySQL) что угодно.

Как вычислить уязвимость, позволяющую внедрять SQL инъекции?


Довольно легко. Например, есть тестовый сайт test.ru. На сайте выводится список новостей, с возможностью детального просомтра. Адрес страницы с детальным описанием новости выглядит так: test.ru/?detail=1. Т.е через GET запрос переменная detail передаёт значение 1 (которое является идентификатором записи в табице новостей).

Изменяем GET запрос на ?detail=1' или ?detail=1". Далее пробуем передавать эти запросы серверу, т.е заходим на test.ru/?detail=1' или на test.ru/?detail=1".

Если при заходе на данные страницы появляется ошибка, значит сайт уязвим на SQL инъекции.

Пример ошибки, возникающей при проверке уязвимости
image

Возможные SQL инъекции (SQL внедрения)
1) Наиболее простые — сворачивание условия WHERE к истиностному результату при любых значениях параметров.
2) Присоединение к запросу результатов другого запроса. Делается это через оператор UNION.
3) Закомментирование части запроса.

Практика. Варианты взлома сайта с уязвимостью на SQL внедрения


Итак, у нас есть уже упоминавшийся сайт test.ru. В базе хранится 4 новости, 3 из которых выводятся. Разрешение на публикацию новости зависит от парметра public (если параметр содержит значение 1, то новость публикуется).

Список новостей, разрешённых к публикации
image

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

Но так как мы уже проверяли сайт на уязвимость и он выдавал ошибку БД, то пробуем перебирать возможные варианты запросов.
В адресной строке плюс (+) выполняет роль пробела, так что не пугайтесь

Тестирую следующие варианты:
test.ru/?detail=4+OR+1
test.ru/?detail=4+--
test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4

В итоге удача улыбнулась и два запроса (первый и третий) вернули нам детальное описание четвёртой новости
image

Разбор примера изнутри


За получение детального описания новости отвечает блок кода:
$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' (т.е сравниваемое значение писать в прямых апострофах).

Глядя на запрос, получаемый при обращении к странице через test.ru/?detail=4+OR+1

SELECT * FROM `news` WHERE `public`='1' AND `id`=4 OR 1 ORDER BY `position` DESC


становится не совсем ясно, почему отобразилась 4-ая новость. Дело в том, что запрос вернул все записи из таблицы новостей, отсортированные в порядке убывания сверху. И таким образом наша 4-ая новость оказалась самой первой, она же и вывелась как детальная. Т.е просто совпадение.

Разбираем запрос, сформированный при обращении через 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, 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);


Статья была подготовлена на основе практических навыков по защите веб-систем. Теория дело хорошее, но практика важнее и главное она работает.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 45
  • +14
    Мне кажется следовало бы упомянуть еще такой способ защиты, как использование prepared statements.
    • +3
      Причём упомянуть в первую очередь. Вообще несколько странно видеть, что люди до сих пор используют в PHP MySQL, когда есть MySQLi и PDO.
      • +1
        Увы не все вольны выбирать технологии с которыми приходится работать. Но раз речь пошла о статье — упомянуть нужно было.
    • –5
      Статья интересная, но еще интереснее она была бы если б разбор полетов проходил на реальном сайте.
      Разумеется, немного подождать пока админ этого сайта залатает дырку.
      • 0
        сайт брался тестовый для примера и на локальном хосте
        за реальный можно и в тюрьму попасть
        • 0
          Ну так вы ничего противозаконного и не показывали. Нашлась дырка, о ней админу сообщается. Он ее правит. И волки целы и овцы сыты :)
          • +3
            Ну за такое
            test.ru/?detail=4+UNION+SELECT+*+FROM+news+WHERE+id=4

            не думаю, что могут посадить
            • –3
              вы так торопитесь выразить своё мнение, что пропускаете важный текст «если б разбор полетов проходил на реальном сайте»
              • +3
                Если даже и на реальном, что в этом плохого? Есть такая статья?
        • +4
          За конкатенацию для подстановки значений в запрос еще в универе били по пальцам (и правильно делали). Используйте параметризованные запросы, и будет вам счастье.
          • +1
            хороший у вас универ был, что такие тонкости пальцеломом присекал
            • 0
              поддерживаю, мне что-то больше попадается не опытных фрилансеров, которые даже понятия «конкатенация» незнают.
              • 0
                Ну я бы не сказал, что это тонкость :)
                • 0
                  ну в стиле преподавания в универе: выдать инфу по взаимодействию php с mysql, а уже подробности — изучайте сами. Не заостряют внимание на этом, да и далеко не каждый студент потом становится веб-прогером
                  • 0
                    Не соглашусь. Стиль преподавания в универе — дать теоретическую базу («научить учиться»), а php и иже с ним уже давать в качестве ознакомительных спецкурсов. Если база есть — освоить его и смежные технологии не составит большого труда.
                    • 0
                      так суть в том, что и ваши слова не противоречат моему первому ответу тред-стартеру
            • +21
              Неужели сотни одинаковых статей на античатах и в журнале хакер и даже тут не достаточно и нужно мусолить и мусолить одно и тоже?
              Ну серьёзно — что нового вы рассказали?
              • +1
                Мало того, автор еще и не до конца раскрыл тему.
                • –1
                  Задача была рассказать, показать пример, разобрать пример.
                  В будущем что-нибудь ещё более подробно разберём.
                  Если хотите, можете написать свою статью с детальным (!) разбором примеров.

                  Кстати, насчёт одинаковых статей. Да, вы правы, на моей домашней странице тоже есть эта же статья. Насчёт античата и прочего — я не читаю статьи о чём-то предметном без наличия скриншотов, фотографий и других картинок.
                  • +2
                    Разберите переполнение буффера в прикладном ПО и, как следствие, выполнение произвольных команд на целевой системе.
                    • –1
                      Спасибо за идею. Действительно это очень интересная тема.
                    • 0
                      кстати, не всегда они и нужны, эти скрины :) думаю тру-линуксоиды поддержат
                      • +3
                        Про статью промолчу, ибо фу.
                        я не читаю статьи о чём-то предметном без наличия скриншотов, фотографий и других картинок.
                        мне вас жаль +)
                  • +3
                    Не sql injection, больше говнокод, но дает почувствовать себя хакером, Задание получить доступ к Админке
                    • 0
                      ' or '1'='1?
                      Или можно идти за чем-то более интересным? :]
                      • 0
                        более). Еще раз говорю там нет базы. Админка есть, но зашифрована.
                        Это просто полигон для отработки и проверки знаний для приема в команду
                        • 0
                          думаю можно было и не комментить)

                          Копаем саму админку. Думал хватит в куках что-нибудь типа «is_admin=1», а нифига
                        • 0
                          Ну ты близко, осталось совсем чуть чуть, найти пароль
                          • 0
                            Ну дальше там легко, алгоритм и все файлы даны. Не интересно :)
                            • +1
                              Угу я тоже так сначала подумал… а ушло на расшифровку около 2 недель
                              • 0
                                заразили же задачей.
                                Странно, что контрольная сумма от значений полного перебора перестановок первых 16 символов ключа не равна нужной сумме.
                                • 0
                                  О да, есть там такое) Вы очень быстро это поняли, у меня это заняло около 2 дней
                      • –1
                        даешь связанные переменные! и не парься. PreparedStatement для Java и CreateCommand в C#
                        • 0
                          Автор PHP рассматривал, как бы :)
                          • 0
                            ну а я вдовесок ещё 2 языка подкинул. Одно гугление выдаст тонны информации, но так ведь по теме
                        • +3
                          по-мойм такие «инъекции» сейчас даже школьники делать умеют, статьями этими весь интернет исписан. Правда должен заметить, что сайты с такими дырами встречаются часто, и я лично знаю людей, которые «программируют» вот так через анус.
                          • +5
                            Прячьте статью в черновики и дописывайте, в данной правке читать нечего.
                            • +11
                              простите, но статья для совсем начинающих PHP-разработчиков
                              есть возможность творить с БД (чаще всего это MySQL) что угодно

                              я бы так не сказал, если это MySQL, то можно лишь читать данные; при наличии привилегии file_priv у текущего пользователя возможно также чтение/запись данных в файловую систему. Добавлять, изменять или удалять информацию не получиться.
                              Далее пробуем передавать эти запросы серверу, т.е заходим на test.ru/?detail=1' или на test.ru/?detail=1".

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

                              это идеальный случай, ничего не сказано про Blind SQL-Injection.
                              Тут название таблицы с новостями (в нашем случае это news) бралось логическим перебором.

                              данный «вариант» взлома очень далек от практики, так как после обнаружения уязвимости атакующий обычно сначала проверяет количество столбцов, затем получает информацию о сервере из version(), user(), etc, также названия баз данных/таблиц, и наконец, собственно, извлекает информацию из БД.
                              Данный процесс называется экранизацией.

                              экранизируют романы, правильно — экранирование.
                              В некоторых конфигурациях PHP этот параметр (магические кавычки) включён, а в некоторых нет.

                              по умолчанию он всегда включен, а в последних версиях PHP этого параметра вообще нет. И это правильно, так как при разработке экранирование нужно делать самому.
                              • +3
                                Какие нфиг magic quotes? Этот гребанный костыль давно выпилили и поделом ему.

                                В топике надо было либо глубже раскрывать вопрос защиты, либо глубже раскрывать вопрос проникновения и поиска. Тот код, что приведён как пример, следует отправить на говнокод. А сам топик в текущей редакции — на свалку.
                                • –1
                                  Хватит уже упоминать покойников а-ля mysql_*. Это, теоретически, полезно как экскурс в историю, вдруг придется ковырять-поддерживать такой вот код. Но не более. А вдруг неофиты прочитают и будут следовать как истине?
                                  • –2
                                    согласен. но я нигде и не упоминал, что она полностью и исчерпывающим образом описывает широкую тему защиты данных. естественно каждый из нас имеет свои фитчи по безопасности, которые старатся применять.

                                    но сами посудите, совесем не важно что использует художник: акварель или масло для написания картины. главное что бы заказ был сделан и он удовлетворял заказчика и базовым требованиям времени.

                                    а вы тут понесли — «не так», «не полностью раскрыто» и.т.д. ясное дело — статья вводная, первая на хабре, абсолютно авторская.
                                    • +2
                                      $id=(int)$_GET['id_news'];

                                      А я бы еще на isset проверил) А вобще, когда я пишу код, я просто не могу обойтись без простых функций вида: (самый примитив)
                                      function _get($key=null,$default=null)
                                      {
                                      if(is_null($key)) return $_GET; //можно произвести какие-то действия
                                      return isset($_GET[$key])? $_GET[$key]: $default; //а тут можно и фильтровать и всё, что угодно
                                      }

                                      к тому же, не возникнет ошибки к несуществующему элементу массива
                                      • 0
                                        Относительно Вашей проверки на число — думаю, стоит почитать статьи про плавающие запятые и угрозы на их основе.

                                        Статья получилась уж слишком вводная.
                                        • 0
                                          В общем, советы по защите те же, только теперь вам стоит немного потратиться на человека, который за деньги проверит ваш сайт на наличие уязвимостей и сообщит вам результаты.

                                          Хочу предложить Вам в помощь сайт для проверки защиты вашего ресурса — по сути фриланс биржа для людей обладающих умением взлома. От вас требуется разместить проект, указать тип уязвимости и бюджет. Дальше просто — ждать предложения выполнить проект от экспертов взлома. Проект молодой и ждёт своих клиентов!

                                          hackmysite.ru/

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