Как хакнуть форму? Sql инъекции. Динамическая работа с данными. Анализ запросов перед приложением

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

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

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

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

Например, вот так выглядит SQL запрос запроса id статьи при поиске:

SELECT id,title,content FROM posts WHERE title LIKE "%запрос_пользователя%"

А теперь представьте, что пользователь вместо ключевых слов из статьи введет такую комбинацию:

1%"; DROP TABLE posts LIKE "%;

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

SELECT id,title,content FROM posts WHERE title LIKE "%1%"; DROP TABLE posts LIKE "% %"

Это сработает с любым запросом, в котором есть ввод данных пользователей, если программист не позаботился о безопасности. Следовательно, чтобы решить эту проблему нужно просто экранировать все кавычки в запросах от пользователя. А теперь перейдем к способам защиты.

Защита сайта от sql инъекций на уровне PHP

Защита от sql атак может выполняться различными способами. Первое, на что стоит обратить внимание и что очень важно - это чтобы программист уже во время написания кода занимался экранированием кавычек с помощью таких функций, как mysql_real_escape_string или mysqli_real_escape_string. Если каждая переменная, которая используется в запросах к базе будет профильтрована ими, программистом или на уровне CMS, то никаких проблем не возникнет.

Но почему же на протяжении последних 14 лет все еще случаются атаки на SQL? Все просто. Программисты ленивы, а делать небезопасные запросы к базе так просто, в то время как безопасные - более сложны. Во всяком случае, сложнее, чем небезопасные.

Защита на уровне веб-сервера

Не всегда есть возможность исправить все недоработки в коде. Например, популярный движок Drupal имеет более 20 000 строк кода, WordPress - 60 000, а Joomla - 180 000. Было бы нецелесообразно все это переписывать. Но можно поступить по-другому. Сначала мы отфильтруем все значения из переменной REQUEST в самом начале скрипта. Вставьте этот код после подключения базы данных:

if (!function_exists("clean")) {
if (get_magic_quotes_gpc()) {
function magicquotes_Stripslashes(&$value, $key) {
$value = stripslashes($value);
}
$gpc = array(&$_COOKIE, &$_REQUEST);
array_walk_recursive($gpc, "magicquotes_Stripslashes");
}
function clean(&$value, $key) {
//эта функция экранирует все кавычки.
$value = mysql_real_escape_string($value);
}
}
$req = array(&$_REQUEST);
array_walk_recursive($req, "clean");

Для PHP 7 вам нужно будет использовать функцию mysqli_real_escape_string, поскольку расширение mysql было удалено из этой версии языка. Для экранирования кавычек используется лишь функция clean и все что ниже нее. То что выше применяется для совместимости с версиями PHP ниже 5.4. В них была настройка Magic Quotes, которая при включении экранировала все кавычки. Чтобы наш скрипт все не портил мы сначала все убираем экранирование если она включена.

Теперь у вас есть дополнительная защита на уровне PHP. Осталось еще позаботиться про защиту на уровне веб-сервера. Если вы используете Nginx, то можно добавить такие правила в вашу секцию server:

set $block_sql_injections 0;
if ($query_string ~ "union.*select.*\(") {
set $block_sql_injections 1;
}
if ($query_string ~ "union.*all.*select.*") {
set $block_sql_injections 1;
}
if ($query_string ~ "concat.*\(") {
set $block_sql_injections 1;
}
if ($block_sql_injections = 1) {
return 403;
}

Здесь мы отфильтровываем все запросы, которые содержат слова select, concat вместе с кавычками. Это явный признак, что пользователь пытается выполнить SQL инъекцию, а значит его запрос нужно заблокировать.

Также вы можете блокировать подозрительные адреса на уровне веб-сервера Apache, например, выбрать самые часто употребляемые ключевые слова SQL. Правда, это опасно, поскольку могут быть заблокированы и запросы обычных пользователей. Добавьте в вашу секцию VitualHost такие строки:

RewriteCond %{QUERY_STRING} [^a-z](declare¦char¦set¦cast¦convert¦delete¦drop¦exec¦insert¦meta¦script¦select¦truncate¦update)[^a-z]
RewriteRule (.*) - [F]

Но это еще не полное решение, здесь можно пойти дальше. Эта блокировка не защищает от SQL инъекций, выполняемых с помощью POST или RESTful запросов. Еще можно активировать модуль mod_security:

sudo a2enmod mod_security

Здесь тоже есть несколько правил, которые защищают от инъекций. Но желательно использовать еще и более комплексный подход.

Разделение базы данных

Чтобы сделать вашу базу данных более безопасной, вы можете разделить ее на несколько частей. В области информационной безопасности существует такое понятие, как принцип минимальных привилегий. Суть принципа состоит в том, что программа или пользователь должны иметь доступ только к тому, что им нужно и нечему больше. Например, будет разумно хранить данные о кредитных картах пользователя и форумы в разных базах данных. Особенно, если формы используют устаревшую версию phpBB. Это своеобразная дополнительная защита sql injection.

Анализ запросов перед приложением

Еще один вариант - это использования более сложных систем защиты. Это может быть аппаратное решение, которое работает поверх iptables или ipfw или же система обнаружения вторжений на сервере HIDS, такая, как OSSEC. Но такое решение намного сложнее, чем нужно и не предназначено для решения нашей задачи. Можно использовать специальные брандмауэры веб-приложений, с помощью которых, в том числе, выполняется защита от SQL инъекций. Это такие свободные решения, как ModSecurity или IronBee.

Выводы

Нет идеального решения или волшебной палочки, с помощью которой бы получалась стопроцентная защита сайта от sql инъекций, хотя PHP стремится быть все более защищенным. Начиная с версии 7.0 была удалена поддержка расширения MySQL. Теперь необходимо переходить на MySqli или PDO. И это хорошо, потому что эти расширения делают проще использование данных с подготовленными операторами. Хотя для этого все еще требуется написать несколько строк.

Существует множество способов выполнения SQL атак, но до тех пор, пока разработчики не будут писать правильный код, а на веб-серверах не будут на максимум использоваться средства защиты, эти атаки не исчезнут из списка ТОП 10 OSWAP. Настройте вашу систему так, чтобы защитить свои данные и базы данных.

Похожие записи:


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 инъекции.

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

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

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

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

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

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

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

Тестирую следующие варианты:
test.ru/?detail=4+OR+1
test.ru/?detail=4+--
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" (т.е сравниваемое значение писать в прямых апострофах).

Глядя на запрос, получаемый при обращении к странице через 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);

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

Представляем вашему вниманию новый курс от команды The Codeby - "Тестирование Веб-Приложений на проникновение с нуля". Общая теория, подготовка рабочего окружения, пассивный фаззинг и фингерпринт, Активный фаззинг, Уязвимости, Пост-эксплуатация, Инструментальные средства, Social Engeneering и многое другое.


Суть SQL-инъекций

Наверное, уже слышали шутку из Интернета: «Почему во всех уроках рисования одно и тоже: Например, урок по рисованию совы. Сначала полчаса долго в деталях рисуем глаз совы. А потом — раз — за пять минут - рисуем оставшуюся часть совы ».

Вот даже картинка по этому поводу есть:

По SQL-инжектам материала море: статьи, книги, видеокурсы (платные и бесплатные). При этом не многие из них прибавляют понимания по этому вопросу. Особенно если вы новичок. Я хорошо помню свои ощущения: вот он кружок, вот он остаток совы…

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

Для опытов, у нас будет очень простой и уязвимый к SQL-инъекции скрипт:

Для доступа к Бобруйской районной библиотеке введите Ваши учётные данные:

Введите ваше имя

Введите ваш пароль


query("SET NAMES UTF8"); $mysqli->query("SET CHARACTER SET UTF8"); $mysqli->query("SET character_set_client = UTF8"); $mysqli->query("SET character_set_connection = UTF8"); $mysqli->query("SET character_set_results = UTF8"); } $name = filter_input(INPUT_GET, "name"); $password = filter_input(INPUT_GET, "password"); if ($result = $mysqli->query("SELECT * FROM `members` WHERE name = "$name" AND password = $password")) { while ($obj = $result->fetch_object()) { echo "

Ваше имя: $obj->name

Ваш статус: $obj->status

Доступные для Вас книги: $obj->books


"; } } else { printf("Ошибка: %sn", $mysqli->error); } $mysqli->close(); ?>

Вы намного больше поймёте, если будете всё делать вместе со мной. Поэтому вот . В нём два файла: index.php и db_library.sql . Файл index.php разместите в любое место на сервере - это и есть наш уязвимый скрипт. А файл db_library.sql нужно импортировать, например, при помощи phpMyAdmin.

В файл index.php в качестве имени пользователя базы данных задан root, а пароль - пустой. Вы можете вписать свои данные, отредактировав строчку:

$mysqli = new mysqli("localhost", "root", "", "db_library");

По легенде, это форма входа в он-лайн версию Бобруйской районной библиотеки. Нам уже дали учётные данные: имя пользователя - Demo, пароль - 111 .

Давайте введём их и посмотрим:

Наши учётные данные приняты, на экраны выведено наше имя, статус и доступные для нас книги. Можете попробовать, с любыми другими данными (если поменять имя или пароль) мы не сможем войти и посмотреть доступные для чтения книги. Также мы не можем узнать, какие книги доступны для остальных, поскольку мы не знаем их имени и пароля.

Подсмотрим в исходный код, чтобы понять, как произошёл запрос к базе данных:

SELECT * FROM `members` WHERE name = "$name" AND password ="$password"

Слово SELECT в SQL-запросе показывает, какие данные нужно получить. Например, можно было бы указать SELECT name, или SELECT name, password. Тогда в первом бы случае из таблицы было бы получено только имя, а во втором - только имя и пароль. Звёздочка говорит, что нужно получить все значения. Т.е. SELECT * — это означает получить все значения.

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

Далее WHERE , если вы изучали какие-либо языки программирования, то это слово больше всего напоминает «Если». А дальше идут условия, эти условия могут быть истинными (1) или ложными (0). В нашем случае

(name = ‘$name’) AND (password =’$password’)

означает, что условие будет истинным, если переданная переменная $name будет равна значению поля name в таблице и переданная переменная ‘$password будет равна значению поля password в таблице. Если хотя бы одно условия не выполняется (неверное имя пользователя или пароль), то из таблицы ничего не будет взято., т. е. выражение SELECT * FROM `members` WHERE name = ‘$name’ AND password =’$password’ означает: в таблице `members` взять значения всех полей, если для них выполняется условие - совпадают переданное имя пользователя и пароль с теми, которые встречаются в таблице.

Это понятно. Давайте теперь, например, с именем пользователя подставим одиночную кавычку:

Адресная строка:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo’&password=111

Никакие данные не получены, вместо них мы видим ошибку:

Ошибка: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near "111"" at line 1

При введении верных данных, наш запрос выглядел так:

SELECT * FROM `members` WHERE name = "Demo" AND password ="111"

При добавлении кавычки, наш запрос превращается в следующее:

SELECT * FROM `members` WHERE name = "Demo" " AND password ="111"

Я поставил дополнительные пробелы для наглядности, т. е. у нас получается запрос

кстати, запрос верный по синтаксису. И сразу после него, без каких либо разделителей идёт продолжение запроса:

" AND password ="111"

Оно-то всё и ломает, поскольку количество открывающих и закрывающих кавычек не равно. Можно, например, подставить ещё одну кавычку:

SELECT * FROM `members` WHERE name = "Demo" " " AND password ="111"

Адресная строка:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo»&password=111

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

Ответ есть - это комментарии.

Комментарии в MySQL можно задать тремя способами:

# (решётка - работает до конца строки)

(два тире - работают до конца строки, нужен символ пробела после двух тире)

/* это комментарий */ группа из четырёх символов - всё, что внутри - это комментарий, всё, что до или после этой группы символов, не считается комментарием.

Давайте в наш запрос с одной кавычкой, после этой кавычки поставим знак комментария, чтобы отбросить хвостик, и знак +, который обозначает пробел, чтобы запрос получился таким:

SELECT * FROM `members` WHERE name = "Demo" --+ " AND password ="111"

Адресная строка:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo’—+&password=111

Ошибка не только исчезла, но и выведены корректные данные для пользователя Demo. Поскольку теперь наш запрос приобрёл вид

SELECT * FROM `members` WHERE name = "Demo"

ведь хвостик —+ ‘ AND password =’111’ превратился в комментарий и больше на запрос не влияет.

Посмотрите ещё раз внимательно на новый запрос:

SELECT * FROM `members` WHERE name = "Demo"

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

К сожалению, я не знаю ни одного легитимного имени и мне нужно придумать что-то другое.

Посмотрим внимательно на эту часть запроса:

WHERE name = "Demo"

Помните про AND, которое используется в первом запросе? Оно означает логическую операции «И». Напомню, логическая операции «И» выдаёт «истина» (1) только если оба выражения являются истиной. Но логический оператор «ИЛИ» выдаёт «истина» (1) даже если хотя бы одно из выражений является истиной. Т.е. выражение

WHERE name = "Demo" OR 1

всегда будет истиной, всегда будет возвращать 1. Поскольку одно из двух сравниваемых выражений всегда возвращает 1.

Т.е. нам нужно составить выражение, которое будет выгладить так:

SELECT * FROM `members` WHERE name = "Demo" OR 1

Адресная строка:

http://localhost/test/mysql-inj-lab1/index.php?name=Demo’ OR 1 —+ &password=111

Результат:

Результат отличный! Мы получили список всех записей в таблице.

ORDER BY и UNION - главные друзья SQL-инъекций

Мы уже сейчас получили данные, которые были недоступны тем, у кого нет валидных имени пользователя и пароля. Можно ли что-то ещё получить? Да, можно получить полный дамп этой таблицы (напомню, у нас по прежнему нет паролей. Более того, мы можем получить все данные из всех баз на этом сервере через одну крошечную дырочку!

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

UNION позволяет довольно гибко объединять SQL-запросы с SELECT, в том числе и от разных баз данных. Но есть важное требование к синтаксису: количество столбцов в первом SELECT должно равняться количеству столбцов во втором SELECT.

ORDER BY задаёт сортировку полученных из таблицы данных. Можно задавать сортировку по имени столбца, а можно по его номеру. Причём, если столбца с таким номером нет, то будет показана ошибка:

Адресная строка:

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 1 —+ &password=111

Запрос выглядит так:

SELECT * FROM `members` WHERE name = "-1" ORDER BY 1

Мы заменили имя пользователя на -1 чтобы не выводились никакие данные.

Ошибки нет, также нет ошибки и при запросах

SELECT * FROM `members` WHERE name = "-1" ORDER BY 2 SELECT * FROM `members` WHERE name = "-1" ORDER BY 3 SELECT * FROM `members` WHERE name = "-1" ORDER BY 4 SELECT * FROM `members` WHERE name = "-1" ORDER BY 5

А вот запрос

SELECT * FROM `members` WHERE name = "-1" ORDER BY 6

ему соответствует адресная стркоа

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ ORDER BY 6 —+ &password=111

Выдал ошибку

Ошибка: Unknown column "6" in "order clause"

Это означает, что из таблицы выбираются данные по пяти колонкам.

Конструируем наш запрос с UNION:

Как я сказал, количество полей должно быть в обоих SELECT одинаковое, а вот что в этих полях - не очень важно. Можно, например, прописать просто цифры - и именно они и будут выведены. Можно прописать NULL – тогда вместо поля ничего не будет выведено.

SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2,3,4,5

Адресная строка:

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,5 —+ &password=111

Другой способ нахождения количества столбцов - с помощью того же UNION. Лесенкой прибавляем количество столбцов:

SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1 SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2 SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2,3 SELECT * FROM `members` WHERE name = "-1" UNION SELECT 1,2,3,4

Все они будут вызывать одну и туже ошибку:

Ошибка: The used SELECT statements have a different number of columns

Делайте так пока не исчезнет сообщение об ошибке.

Обратите внимание, что содержимое некоторых полей UNION SELECT 1,2,3,4,5 выводится на экран. Вместо цифр можно задать функции.

Что писать в SELECT

Есть некоторые функции, которые можно писать непосредственно в UNION:

  • DATABASE() — показать имя текущей базы данных
  • CURRENT_USER() — показывает имя пользователя и имя хоста
  • @@datadir - выводит абсолютный путь до базы данных
  • USER() — имя пользователя
  • VERSION() — версия базы данных

В нашем примере выводятся поля 2, 4 и 5. Т.е. мы можем использовать любое из этих полей.

Используем DATABASE() в UNION SELECT

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,DATABASE() —+ &password=111

Результат:

Используем CURRENT_USER() в UNION SELECT

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,CURRENT_USER() —+ &password=111

Результат:

Используем @@datadir в UNION SELECT

http://localhost/test/mysql-inj-lab1/index.php?name=-1′ UNION SELECT 1,2,3,4,@@datadir —+ &password=111

Результат:

Получение имён таблицы, полей и дамп базы данных

В базе данных information_schema есть таблица, которая называется tables . В этой таблице содержится список всех таблиц, которые присутствуют во всех базах данных этого сервера. Мы можем отобрать наши таблицы, ища в поле table_schema название нашей базы данных - ‘db_library’ (имя мы узнали с помощью DATABASE()).

Это называется полная техника UNION. Материала по ней предостаточно в Интернете. На моём же MySQL сервере полная техника UNION не работает. У меня появляется ошибка

Ошибка: Illegal mix of collations for operation "UNION"

Не работает не из-за кривизны рук, поскольку у sqlmap также эта техника не приносит результатов:

Something went wrong with full UNION technique (could be because of limitation on retrieved number of entries). Falling back to partial UNION technique

Возможно, это связано с версией MySQL 5.6. Т.к. привести практических примеров я не могу, а переписывать чужие неработающие команды мне не интересно - сейчас и без меня в Интернете развелось «великих теоретиков» сколько угодно, то я решил сразу перейти к рассмотрению частичной технике UNION. Но это не самая простая техника, да и статья уже получилась достаточно большой.

п.с. ах да, забыл про LIMIT. Тоже в следующий раз расскажу о роли LIMIT в SQL-инъекциях.

Гарант является доверенным посредником между Участниками при проведении сделки.


SQL инъекция — это атака, которая задействует динамические операторы SQL , вынося в комментарии определенные части инструкций или добавляя условие, которое всегда будет истинным. Она нацелена на дыры в архитектуре веб-приложений и использует операторы SQL для выполнения вредоносного SQL-кода :

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

Как работает SQL-инъекциях

Типы атак, которые могут быть выполнены с использованием SQL-инъекции , различаются по типу поражаемых механизмов базы данных. Атака нацеливается на динамические операторы SQL . Динамический оператор — это оператор, который создается во время выполнения на основе параметров из веб-формы или строки запроса URI .

Рассмотрим простое веб-приложение с формой входа. Код HTML-формы приведен ниже:

  • Форма принимает адрес электронной почты, а затем пароль отправляется в файл PHP с именем index.php ;
  • Сессия хранится в файле cookie . Эта возможность активируется при установке флажка remember_me . Для отправки данных используется метод post . Это означает, что значения не отображаются в URL-адресе .

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

  • Запрос использует значения массива $ _POST напрямую, не санируя его;
  • Пароль шифруется с использованием алгоритма MD5 .

Мы рассмотрим атаку с использованием SQL инъекции sqlfiddle . Откройте в браузере URL-адрес http://sqlfiddle.com/ . На экране появится следующее окно.

Примечание : вам нужно будет написать инструкции SQL :


Шаг 1. Введите этот код в левую панель:

CREATE TABLE `users` (`id` INT NOT NULL AUTO_INCREMENT, `email` VARCHAR(45) NULL, `password` VARCHAR(45) NULL, PRIMARY KEY (`id`)); insert into users (email,password) values ("[email protected]",md5("abc"));

Шаг 2. Нажмите кнопку «Build Schema ».
Шаг 3. Введите приведенный ниже код в правой панели:

select * from users;

Шаг 4. Нажмите «Run SQL ». Вы увидите следующий результат:

Предположим, что пользователь предоставляет адрес электронной почты [email protected] и 1234 в качестве пароля. Запрос, который должен быть выполнен в базе данных, может выглядеть следующим образом:

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

[email protected]" OR 1 = 1 LIMIT 1 -- " ]

и xxx в поле пароля.

Сгенерированный динамический оператор будет выглядеть следующим образом:

  • [email protected] заканчивается одной кавычкой, которая завершает строку;
  • OR 1 = 1 LIMIT 1 — это условие, которое всегда будет истинным, оно ограничивает возвращаемые результаты только одной записью.

0; ‘ AND … — это комментарий SQL , который исключает часть пароля.

Скопируйте приведенный выше запрос и вставьте его в текстовое поле SQL FiddleRun SQL , как показано ниже:


Хакерская активность: SQL-инъекции в веб-приложения

У нас есть простое веб-приложение, доступное по адресу http://www.techpanda.org/ , которое специально сделано уязвимым для атак с использованием SQL инъекций для новичков в демонстрационных целях. Код HTML-формы , приведенный выше, взят со страницы авторизации данного приложения.

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

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


Предположим, что злоумышленник предоставляет следующие данные:

Шаг 1 : Вводит [email protected] в качестве адреса электронной почты;
Шаг 2 : Вводит xxx’) OR 1 = 1 — ] ;


Нажимает кнопку «Отправить ».

Он будет направлен в панель администрирования. Сгенерированный запрос будет выглядеть следующим образом:

На приведенной ниже диаграмме показано, как запрос был сгенерирован:


  • В запросе предполагается, что используется шифрование md5 ;
  • Используется закрывающаяся одиночная кавычка и скобка;
  • К оператору добавляется условие, которое всегда будет истинным.

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

Другие типы атак с использованием SQL-инъекций

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

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

Приведенный выше список не является полным. Он просто дает представление о том, какую опасность представляют SQL-инъекции .

Инструменты для автоматизации SQL-инъекций

В приведенном выше примере мы использовали методы ручной атаки. Перед тем, как сделать SQL инъекцию , нужно понимать, что существуют автоматизированные инструменты, которые позволяют выполнять атаки эффективнее и быстрее:

  • SQLSmack ;
  • SQLPing 2 ;
  • SQLMap .

Как предотвратить SQL-инъекции

Вот несколько простых правил, которые позволят защититься от атак с использованием SQL-инъекций :

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

Хранимые процедуры — они могут инкапсулировать SQL-запросы и обрабатывать все входные данные в качестве параметров.

Подготовленные запросы — сначала создаются запросы, а затем все предоставленные пользовательские данные обрабатываются в качестве параметров. Это не влияет на синтаксис инструкции SQL .

Регулярные выражения — могут быть использованы для обнаружения потенциально вредоносного кода и его удаления перед выполнением операторов SQL .

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

Сообщения об ошибках — не должны раскрывать конфиденциальную информацию. Простые пользовательские сообщения об ошибках, такие как «Извините, возникла техническая ошибка. Служба поддержки уже уведомлена о ней. Повторите попытку позже », можно использовать вместо отображения запросов SQL , вызвавших ошибку.

Хакерская активность: использование для SQL-инъекций Havij

В этом практическом сценарии мы собираемся использовать программу Havij Advanced SQL Injection для сканирования уязвимостей сайта.

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


Упомянутый выше инструмент можно использовать для оценки уязвимости / приложения.

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

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

Пример уязвимости

Предположим, имеется скрипт, отображающий список пользователей из данного города, принимающий в качестве GET-параметра id города. Обращение к скрипту будет происходить с помощью HTTP по адресу /users.php?cityid=20

В скрипте выше разработчик вставляет GET-параметр в SQL-запрос, подразумевая, что в GET-параметре всегда будет число. Злоумышленник может передать в качестве параметра строку и тем самым повредить запрос. Например, он обратится к скрипту как /users.php?cityid=20; DELETE * FROM users
SQL-запрос получится таким:

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

Как защититься?

Давайте заключим пользователькую информацию в одинарные кавычки. Поможет ли это?

Из примера выше видно, что заключить в одиночные кавычки недостаточно. Необходимо также экранировать все кавычки, содержащиеся в строке. Для этого в PHP предусмотрена функция mysql_real_escape_string(), которая добавляет обратный слеш перед каждой кавычкой, обратной кавычкой и некоторыми другим спецсимволами. Рассмотрим код:

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

Если известно, что параметр должен принимать числовое значение числовым, его можно привести к числовому виду явно с помощью функции intval() или floatval() . В данном примере мы могли бы использовать:

$sql = "SELECT username, realname
FROM users
WHERE cityid=""
.intval ( $_GET [ "cityid" ]) .""" ;

Отличия mysql_real_escape_string() и mysql_escape_string()

mysql_real_escape_string() является усовершенствованной версией функции mysql_escape_string(), широко применяемой для формирования безопасных запросов к БД MySQL. Отличия этих двух функций в том, что mysql_real_escape_string() правильно работает с многобайтовыми кодировками.

Предположим, в обрабатываемых данных есть символ (скажем, в UTF-8), код которого состоит из двух байт — шестнадцатеричных 27 и 2B (десятичные 39 и 43 соответственно). mysql_escape_string() воспринимает каждый байт передаваемых ей данных как отдельный символ (точнее, как код отдельного символа) и решит, что последовательность байт 27 и 2B — это два разных символа: одинарная кавычка (") и плюс (+). Поскольку функция воспринимает кавычку как специальный символ, перед байтом с кодом 27, который на самом деле является частью какого-то безобидного символа, будет добавлен слэш (\). В результате данные отправятся в базу в искаженном виде.

Стоит отметить, что mysql_real_escape_string() работает правильно во всех случаях и может полностью заменить mysql_escape_string().

mysql_real_escape_string() доступна в PHP с версии 4.3.0.

Дополнительные примеры

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

Инъекция в сложных запросах

В простейшем примере была возможность встроить код в конец SQL-запроса. На практике в конце SQL-запроса могут быть дополнительные условия, операторы сортировки, группировки и другие SQL-конструкции. В каждом конкретном случае, злоумышленник постарается встроить вредоносный кусок таким образом, чтобы запрос в целом остался синтаксически корректным, но выполнял другую функцию. Здесь мы рассмотрим простейший пример уязвимого запроса с дополнительным условием.

В результате условие age<35 не будет влиять на выборку, т.к. оператор OR имеет более низкий приоритет, чем AND, и WHERE из приведённого выше запроса по-другому можно записать в виде WHERE (cityid="20" AND 1 ) OR ("1" AND age<"35" ) (напомним, что выражение WHERE 1 истинно всегда). В результате под условие подойдут и те строки, у которых cityid="20", и те, у которых age<35, причем наличие последних не обязательно.

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

Результаты запроса не отображаются пользователю

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

$sql = "SELECT count(*)
FROM users
WHERE userid=""
.$_GET [ "userid" ] .""" ;

Запрос выше всего лишь проверяет наличие пользователя с данным userid: если он возвращает любую отличную от нуля величину — показывается профиль пользователя с соответствующим userid, если же возвращён 0 (то есть, нет пользователей, удовлетворяющих критерию запроса) — сообщение "пользователь не найден".

В этом случае определение пароля (или другой информации) производится перебором. Взломщик передает в качестве параметра userid строку 2" AND password LIKE "a% . Итоговый запрос:

SELECT count (*) FROM users WHERE userid= "2" AND password LIKE "a% "

Взломщик получит "пользователь не найден", если пароль не начинается на букву "a", или стандартную страницу с профилем пользователя, в противном случае. Перебором определяется первая буква пароля, затем вторая и.т.д.

Выводы

  • Все запросы, использующие внешние данные, требуется защитить от SQL-инъекций. Внешние данные могут быть переданы не только в качестве GET-параметров, но и методом POST, взяты из COOKIE, со сторонних сайтов или из базы данных, в которую пользователь имел возможность занести информацию.
  • Все числовые параметры следует явно преобразовывать в числовой вид с помощью функций intval() и floatval()
  • Все строковые параметры следует экранировать с помощью mysql_real_escape_string() и заключать в кавычки.
  • Если построить SQL-инъекцию сложно, не следует ожидать, что злоумышленник не догадается как это сделать. Особенно это относится к движкам, исходный код которых является публичным.

Удачи в построении безопасных приложений!