Главная / Injetion / Sql injection

Что такое SQL Injection

Внедрение SQL-кода (англ. SQL injection) — один из распространённых способов взлома сайтов и программ, работающих с базами данных, основанный на внедрении в запрос произвольного SQL-кода.

Внедрение SQL, в зависимости от типа используемой СУБД и условий внедрения, может дать возможность атакующему выполнить произвольный запрос к базе данных (например, прочитать содержимое любых таблиц, удалить, изменить или добавить данные), получить возможность чтения и/или записи локальных файлов и выполнения произвольных команд на атакуемом сервере.

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

При помощи SQL инъекций есть возможность "зомбировать" СУБД для коварных целей, например сделать частью ботнета

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

Предположим есть сайт на котором есть новостной раздел. При просмотре новости мы попадаем на url вида site.hach/news/?id=1. Ниже представлен уязвимый кусок php кода который подгружает новость из БД.

$id  = $_GET['id'];
$sql = "SELECT `id` FROM `news` WHERE `id` = '$id'";

Таким образом, при запорсе страницы site.hach/news/?id=1 в СУБД уйдет следующий запрос:

SELECT `id` FROM `news` WHERE `id` = '1'

Но стоит нам запростить страницу вида site.hach/news/?id=1'; DELETE FROMnews-- и наш запрос будет иметь следующий вид

SELECT `id` FROM `news` WHERE `id` = '1'; DELETE FROM `news` -- '

Таким образом в СУБД уйдет уже 2 запроса, и второй из них - DELETE который удалит все записи новостей

Защита от SQL инъекций

Проблема вышеприведенного PHP кода заключается в том, что данные полученные от пользователя никак не фильтруются. Все данные которые получаемые от пользователя необходимо в обязательном порядке валидировать и фильтровать!

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

$id  = intval($_GET['id']);
$sql = "SELECT `id` FROM `news` WHERE `id` = '$id'";

Таким образом, при попытке внедрить SQL инъекцию все содержимое $_GET['id'] будет преобразовано в целое число и в базу уйдет нормальный запрос.

SELECT `id` FROM `news` WHERE `id` = '1'

Но как писалось выше, это не универсальный способ защитить приложение от SQL инъекций. Это скорее костыль.

Выделяют 2 способа защиты от SQL инъекций.
Эскэйпинг параметров и Параметризованные запросы

Эскэйпинг параметров

Эскейпинг - экранирование специальных символов в строке для предотвращения разрыва SQL запроса Далее будет описан пример использования эскейпинга параметров при использовании модуля PHP - mysqli. В mysqli как и во многих других модулях для работы с БД есть методы эскейпинга. Например real_escape_string. Ниже приведен пример использования real_escape_string.

$id  = $mysqli->real_escape_string($_GET['id']);
$sql = "select * from news where id = '{$id}';";

Таким образом наш SQL код, который уйдет в СУБД будет выглядеть следующим образом

SELECT `id` FROM `news` WHERE `id` = '1\'; drop table news -- ';

Можно заметить что метод real_escape_string экранировал одинарную кавычку которая ранее разрывала наш запрос и делила его на 2 запорса. В данном случае мы не найдем новость, но это не страшно т.к. злоумышленник знал на что идет, но не получил ожидаемого.

Параметризованные (подготавливаемые) запросы

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

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

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

Ниже приведены 2 примера параметризованного запроса с использованием PDO

$sql = "SELECT `id` FROM `news` WHERE `id` = ?";
$stm = $pdo->prepare(sql);
$id  = $_GET['id'];

$stm->execute([$id]);
$result = $stm->fetchAll();

$sql = "SELECT `id` FROM `news` WHERE `id` = :id";
$stm = $pdo->prepare(sql);
$id  = $_GET['id'];

$stm->execute([':id' => $id]);
$result = $stm->fetchAll();