«SQL-запросы и SQL-
инъекции» Приготовил QA Engineer: Зязюля Прохор
Введение
SQL является прежде всего информационно-логическим языком, предназначенным
для описания, изменения и извлечения данных, хранимых в реляционных базах
данных. SQL можно назвать языком программирования, при этом он не является
тьюринг-полным, но вместе с тем стандарт языка спецификацией SQL/PSM
предусматривает возможность его процедурных расширений.
Изначально SQL был основным способом работы пользователя с базой данных и позволял выполнять следующий набор
операций:
создание в базе данных новой таблицы;
добавление в таблицу новых записей;
изменение записей;
удаление записей;
выборка записей из одной или нескольких таблиц (в соответствии с заданным условием);
изменение структур таблиц.
Со временем SQL усложнился — обогатился новыми конструкциями, обеспечил возможность описания и управления новыми
хранимыми объектами (например, индексы, представления, триггеры и хранимые процедуры) — и стал приобретать черты,
свойственные языкам программирования.
При всех своих изменениях SQL остаётся единственным механизмом связи между прикладным программным обеспечением и
базой данных. В то же время современные СУБД, а также информационные системы, использующие СУБД, предоставляют
пользователю развитые средства визуального построения запросов.
Достоинства
Обратите внимание на то, что SQL сам по себе не является ни СУБД, ни отдельным
продуктом. Это язык, применяемый для взаимодействия с СУБД и являющийся в
определенном смысле ее неотъемлемой частью.
Интерактивный и встроенный SQL
CREATE DATABASE – создать базу данных; Операторы определения объектов базы данных содержат операторы,
ALTER DATABASE – изменить базу данных; позволяющие создавать, изменять и уничтожать базы данных и
DROP DATABASE – удалить базу данных; объекты внутри них (таблицы, представления и др.).
CREATE SCHEMA – создать схему базы данных;
DROP SСHEMA – удалить схему базы
данных;
CREATE INDEX – создать индекс для данного поля;
DROP INDEX – удалить существующий
индекс;
CREATE TABLE – создать таблицу;
ALTER TABLE – изменить
таблицу;
DROP TABLE – удалить таблицу;
CREATE DOMAIN – создать домен;
ALTER DOMAIN – изменить домен;
DROP DOMAIN – удалить домен;
CREATE COLLATION – создать
последовательность;
DROP COLLATION – удалить
последовательность;
CREATE VIEW – создать
представление;
DROP VIEW – удалить
представление;
CREATE TRIGGER – создать
триггер;
ALTER TRIGGER – изменить
триггер;
DROP TRIGGER – удалить триггер.
Операторы DDL. CREATE DATABASE.
IF NOT EXISTS - если не указать этот параметр, то при попытке создания базы данных с уже существующим именем, возникнет ошибка выполнения команды.
CHARACTER SET - используется для задания стандартной кодировки таблицы и порядка сортировки.
Если при создании таблицы эти параметры не указываются, то кодировка и порядок сортировки вновь создаваемой таблицы берутся из значений,
указанных для всей базы данных. Если задан параметр CHARACTER SET, но не задан параметр COLLATE, то используется стандартный порядок сортировки. Если
задан параметр COLLATE, но не задан CHARACTER SET, то кодировку определяет первая часть имени порядка сортировки в COLLATE.
Кодировка, заданная в CHARACTER SET, должна поддерживаться сервером (latin1 или sjis), а порядок сортировки должен быть допустимым для текущей
кодировки.
Примеры использования CREATE DATABASE:
Следующий пример создает базу данных "my_db":
CREATE DATABASE `my_db` или CREATE DATABASE `my_db` CHARACTER SET utf8 COLLATE utf8_general_ci;
При создании новой базы данных следует придерживаться некоторых правил относительно имени базы данных:
Максимальная длина имени не должна превышать 64 символов.
Разрешены любые символы, которые допускаются в имени каталога, за исключением / (слеш) и . (точка). Но следует принять во внимание, что нельзя
использовать символы ASCII(0) и ASCII(255).
Операторы DDL. ALTER DATABASE.
Оператор ALTER DATABASE используется для изменения характеристик базы данных. После создания базы данных мы можем изменить ее свойства,
используя оператор ALTER DATABASE. Пользователь должен иметь права администратора для изменения базы данных.
Синтаксис оператора ALTER DATABASE:
ALTER DATABASE database_name [COLLATE collation_name ]
CREATE INDEX – данный запрос используется для создания индексов в таблице. Индексы позволяют быстрее находить нужные данные без чтения всей
таблицы данных. Пользователь не может увидеть индексы, они просто используются для ускорения поиска/запроса.
Синтаксис CREATE INDEX.
Создания индекса в таблице. Повторяющиеся значения допускаются:
CREATE INDEX index_name ON table_name (column_name)
Если вы хотите создать комбинированный индекс, то можно перечислить имена столбцов в скобках через запятую:
1.CREATE INDEX Pindex
2.ON Persons (LastName, FirstName)
Обновления таблицы с индексами занимает большее время, чем обновления таблицы без индексов (потому что индексы тоже
нуждаются в обновлении). Итак, рекомендуется создавать только один индекс на таблицу (один столбец), в котором будет часто
производится поиск.
Операторы DDL. DROP INDEX.
DROP INDEX - данный запрос используется для удаления индексов в таблице. Индексы, таблицы и базы данных могут быть легко удалены с помощью запроса
DROP.
Синтаксис DROP INDEX.
Синтаксис DROP INDEX для MS Access:
DROP INDEX index_name ON table_name
TRUNCATE TABLE - данный запрос используется для удаления данных внутри таблицы, не трогая саму
таблицу. TRUNCATE TABLE table_name
Обновления таблицы с индексами занимает большее время, чем обновления таблицы без индексов (потому что индексы тоже
нуждаются в обновлении). Итак, рекомендуется создавать только один индекс на таблицу (один столбец), в котором будет часто
производится поиск.
Операторы DDL. CREATE TABLE
CREATE TABLE - данный запрос используется для создания таблицы в базе данных.
Синтаксис CREATE TABLE:
1. CREATE TABLE table_name (
2. column_name1 data_type,
3. column_name2 data_type,
4. column_name3 data_type,
5. ....
6. )
Столбец P_Id имеет тип INT. LastName, FirstName, Address и City имеют тип VARCHAR с максимальной длиной
255 символов.
Операторы DDL. ALTER TABLE.
ALTER TABLE - данный запрос используется для добавления, удаления или модификации столбца в уже существующей таблице.
Синтаксис ALTER TABLE:
Для добавления столбца в таблицу, используйте следующий синтаксис:
1. ALTER TABLE table_name
2. ADD column_name datatype
Для удаления столбца в таблице, используйте следующий синтаксис (не все базы данных позволяют удалять один
столбец):
1. ALTER TABLE table_name
2. DROP COLUMN column_name
Изменим тип данных столбца "DateOfBirth" в таблице "Persons": table_name – имя таблицы в базе данных.
1. ALTER TABLE Persons
2. ALTER COLUMN DateOfBirth year column_name – имя столбца.
data_type – определяет, к какому типу данных относится столбец.
Удалим столбец "DateOfBirth" из таблицы "Persons":
1. ALTER TABLE Persons
2. DROP COLUMN DateOfBirth
Операторы DDL. CREATE TRIGGER.
Подсказка: Звездочка (*)
представляет собой самый
быстрый способ выбора всей
колонки!
Оператор SELECT
Например, можно запросить только те товары, цены которых не более 100 денежных единиц:
1. SELECT НаимТовара as [Наименование товара], Цена1 as [Цена закупочная]
2. FROM Товары
3. WHERE Цена < 100
Здесь к тем элементам SQL-запроса, которые уже рассмотрены, добавлено необязательное предложение ORDER BY. Как следует из синтаксиса инструкции
SELECT, используя слова ASC и DESC, можно изменять «направление» сортировки («по возрастанию» и «по убыванию» соответственно). Сортировать можно по
нескольким полям (сначала по одному, затем — по другому, и так далее) и даже по различным элементам одного и того же поля с использованием функций.
В следующем примере используется сортировка выводимого набора по наименованиям:
1. SELECT a.НаимТовара AS [Наименование товара], b.Количество AS [Количество]
2. FROM Товары а, Запасы b
3. WHERE а.КодТовара = b.КодТовара AND b.КодПодразделения='0429'
4. ORDER BY а.НаимТовара
Оператор SELECT
В инструкции SELECT для объединения значений в группы используется конструкция GROUP BY.
Следующий пример имеет результатом список складов и суммарные количества товаров на каждом складе:
1. SELECT с.НаимПодразделения, SUM(b.Количество) AS Количество
2. FROM Запасы b, Подразделения c
3. WHERE b.КодПодразделения = с.КодПодразделения
4. GROUP BY с.НаимПодразделения
Предложение HAVING налагает некоторые условия на выбранные посредством предложения GROUP BY данные.
Следующий пример позволяет получить список складов и суммарное количество товаров на них. Причем в список включаются только те склады, на которых
суммарное количество товаров меньше чем 10000:
1. SELECT с.НаимПодразделения, SUM(b.Количество) AS Количество
2. FROM Запасы b, Подразделения c
3. WHERE b.КодПодразделения = с.КодПодразделения
4. GROUP BY с.НаимПодразделения
5. HAVING SUM(b.Количество) < 10000
Операторы DML. INSERT INTO.
Следующий SQL запрос добавит новую строку, но данные будут содержатся только в столцах "P_Id", "LastName" и "FirstName":
1. INSERT INTO Persons (P_Id, LastName, FirstName)
2. VALUES (5, 'Tjessem', 'Jakob')
Операторы DML. UPDATE.
UPDATE - используется для обновления записей в таблице.
Синтаксис UPDATE:
1. UPDATE table_name
2. SET column1=value, column2=value2,... Замечание: Обратите внимание на WHERE в синтаксисе UPDATE. WHERE определяет, какие записи
3. WHERE some_column = some_value должны быть обновлены, если условие WHERE не прописано, то все записи в таблице будут обновлены!
Можно удалить все записи в таблице, не удаляя саму таблицу. Это означает, что структура таблицы: атрибуты, индексы будут неповрежденными:
1. DELETE FROM table_name
2. OR
3. DELETE * FROM table_name
Замечание: Будьте очень осторожны при выполнении таких запросов. Поскольку записи могут быть потеряны навсегда.
Операторы TCL Операторы TCL (Transaction Control Language)
многоточие «...» определяет, что выражение, предшествующее ему, может повторяться любое число раз;
Полезная информация:
1. Конкатенация — это операция склеивания объектов линейной структуры, обычно строк.
2.Fingerprinting (фингерпринтинг) – это идентификация пользователя не по специальным меткам, сохраненным на
его системе, а по уникальным особенностям его браузера, системы и устройства.
Принцип атаки
Серверное ПО, получив входной параметр id, использует его для создания SQL-запроса. Рассмотрим следующий PHP-скрипт:
1. $id = $_REQUEST['id’];
2. $res = mysqli_query("SELECT * FROM news WHERE id_news = " . $id);
Если на сервер передан параметр id, равный 5 (например так: http://example.org/script.php?id=5), то выполнится следующий SQL-запрос:
1. SELECT * FROM news WHERE id_news = 5
Таким образом, изменение входных параметров путём добавления в них конструкций языка SQL вызывает изменение в логике
выполнения SQL-запроса (в данном примере вместо новости с заданным идентификатором будут выбраны все имеющиеся в базе
новости, поскольку выражение 1=1 всегда истинно — вычисления происходят по кратчайшему контуру в схеме).
Внедрение в стоковые параметры
Серверное ПО, получив запрос на поиск данных в новостях параметром search_text, использует его в следующем SQL-запросе
(здесь параметры экранируются кавычками):
1. $search_text = $_REQUEST['search_text’];
2. $res = mysqli_query("SELECT id_news, news_date, news_caption, news_text, news_id_author FROM news WHERE news_caption LIKE('%
$search_text%')");
Но, внедрив в параметр search_text символ кавычки (который используется в запросе), мы можем кардинально изменить поведение
SQL-запроса. Например, передав в качестве параметра search_text значение ')+and+(news_id_author='1, мы вызовем к
выполнению запрос: 1. SELECT id_news, news_date, news_caption, news_text, news_id_author
2. FROM news
3. WHERE news_caption LIKE('%') and (news_id_author='1%')
Использование UNION
Язык SQL позволяет объединять результаты нескольких запросов при помощи оператора UNION. Это предоставляет возможность
получить несанкционированный доступ к данным. Рассмотрим скрипт отображения новости (идентификатор новости, которую
необходимо отобразить, передается в параметре id):
1. $res = mysqli_query("SELECT id_news, header, body, author FROM news
WHERE id_news = " . $_REQUEST['id']);
Если передать в качестве параметра id конструкцию -1 UNION SELECT 1,username, password,1 FROM admin, это вызовет
выполнение SQL-запроса:
1. SELECT id_news, header, body, author
2. FROM news
3. WHERE id_news = -1 UNION SELECT 1,username,password,1 FROM admin
Так как новости с идентификатором −1 заведомо не существует, из таблицы news не будет выбрано ни одной записи, однако в результат
попадут записи, несанкционированно отобранные из таблицы admin в результате инъекции SQL.
Использование UNION+group_concat()
В некоторых случаях можно провести атаку, но нельзя видеть более одной колонки. В случае с MySQL можно воспользоваться
функцией:
1. group_concat(col, symbol, col)
которая объединяет несколько колонок в одну. Например, для примера данного выше вызов функции будет таким:
1. -1 UNION SELECT group_concat(username, 0x3a, password) FROM admin
Экранирование хвоста
Зачастую, SQL-запрос, подверженный данной уязвимости, имеет структуру, усложняющую или препятствующую использованию
UNION. Например скрипт:
1. $res = mysqli_query("SELECT author FROM news WHERE id=" .
$_REQUEST['id'] . " AND author LIKE ('a%')");
отображает имя автора новости по передаваемому идентификатору id только при условии, что имя начинается с буквы а, и внедрение
кода с использованием оператора UNION затруднительно.
В таких случаях используется метод экранирования части запроса при помощи символов комментария(/* или -- в зависимости от типа
СУБД). В данном примере, можно передать в скрипт параметр id со значением -1 UNION SELECT password FROM admin/*, выполнив
таким образом запрос:
1. SELECT author FROM news WHERE id=-1 UNION SELECT password FROM
admin/* AND author LIKE ('a%')
в котором часть запроса (AND author LIKE ('a%')) помечена как комментарий и не влияет на выполнение.
Расщепление
Для разделения команд в языке SQL используется символ ; (точка с запятой), внедряя этот символ в запрос, пользователь получает
возможность выполнить несколько команд в одном запросе, однако не все диалекты SQL поддерживают такую возможность.
Например, если в параметры скрипта:
1. $id = $_REQUEST['id’];
2. $res = mysqli_query("SELECT * FROM news WHERE id_news = $id");
пользователем передается конструкция, содержащая точку с запятой, например 12;INSERT INTO admin (username, password)
VALUES ('HaCkEr', 'foo'); то в одном запросе будут выполнены 2 команды:
1. SELECT * FROM news WHERE id_news = 12;
2. INSERT INTO admin (username, password) VALUES ('HaCkEr', 'foo');
На данном этапе пользователь изучает поведение скриптов сервера при манипуляции входными параметрами с целью обнаружения
их аномального поведения. Манипуляция происходит всеми возможными параметрами:
Данными, передаваемыми через методы POST и GET;
Значениями [HTTP-Cookie];
HTTP_REFERER (для скриптов);
AUTH_USER и AUTH_PASSWORD (при использовании аутентификации).
Как правило, манипуляция сводится к подстановке в параметры символа одинарной (реже двойной или обратной) кавычки.
Аномальным поведением считается любое поведение, при котором страницы, получаемые до и после подстановки кавычек,
различаются (и при этом не выведена страница о неверном формате параметров). Наиболее частые примеры аномального поведения:
выводится сообщение о различных ошибках;
при запросе данных (например, новости или списка продукции) запрашиваемые данные не выводятся вообще, хотя страница отображается и т.д.
Следует учитывать, что известны случаи, когда сообщения об ошибках, в силу специфики разметки страницы, не видны в браузере,
хотя и присутствуют в её HTML-коде.
Защита от SQL-инъекций
Чтобы внедрение кода (закрытие строки, начинающейся с кавычки, другой кавычкой до её завершения текущей закрывающей
кавычкой для разделения запроса на две части) было невозможно, для некоторых СУБД, в том числе, для MySQL, требуется брать в
кавычки все строковые параметры. В самом параметре заменяют кавычки на \", апостроф на \', обратную косую черту на \\ (это
называется «экранировать спецсимволы»). Это можно делать таким кодом:
В случае когда поле id имеет числовой тип, и его чаще всего не берут в кавычки. Поэтому «закавычивание» и замена спецсимволов на
escape-последовательности не проходит. В таком случае помогает проверка типа; если переменная id не является числом, запрос
вообще не должен выполняться.
Для внесения изменений в логику выполнения SQL-запроса требуется внедрение достаточно длинных строк. Так, минимальная длина
внедряемой строки в вышеприведённых примерах составляет 8 символов («1 OR 1=1»). Если максимальная длина корректного
значения параметра невелика, то одним из методов защиты может быть максимальное усечение значений входных параметров.
Например, если известно, что поле id в вышеприведённых примерах может принимать значения не более 9999, можно «отрезать
лишние» символы, оставив не более четырёх:
1. statement := 'SELECT * FROM users WHERE id = ' + LeftStr(id, 4) + ';';
Использование параметризованных запросов
Многие сервера баз данных поддерживают возможность отправки параметризованных запросов (подготовленные выражения). При
этом параметры внешнего происхождения отправляются на сервер отдельно от самого запроса либо автоматически экранируются
клиентской библиотекой. Для этого используются
на Delphi — свойство
TQuery.Params;
на Perl — через DBI::quote или
DBI::prepare;
на Java — через класс PreparedStatement;
на C# — свойство SqlCommand.Parameters;
на PHP — MySQLi (при работе с
MySQL), PDO. 1. var
2. sql, param : string;
3. begin
4. sql := 'select :text as value from dual’;
5. param := 'alpha’;
6. Query1.Sql.Text := sql;
7. Query1.ParamByName('text').AsString := param;
8. Query1.Open;
9. ShowMessage(Query1['value']);
10. end;
Источники информации