Что такое SQL-инъекция?

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

Существует несколько типов SQL-инъекций: union-based, boolean-based, time-based и error-based. В этой статье мы детально разберём error-based SQL-инъекцию на примере строки:

%")) AND EXTRACTVALUE(5401,CONCAT(0x7e,((SELECT (ELT(5401=5401,1)))),0x7e))-- -

Эта строка — классический пример эксплуатации уязвимости в СУБД MySQL с использованием функций EXTRACTVALUE, CONCAT и ELT.

Как работает error-based инъекция?

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

В MySQL есть функции, которые при определённых условиях вызывают ошибку с текстом, зависящим от переданных аргументов. Это позволяет «вытащить» результат подзапроса в сообщении об ошибке.

Функция EXTRACTVALUE

EXTRACTVALUE(XML_frag, XPath_expr) — функция MySQL для извлечения значения из XML-фрагмента по заданному XPath-выражению. Она возвращает текст, соответствующий XPath. Однако если XPath-выражение некорректно, MySQL генерирует ошибку.

В контексте инъекции EXTRACTVALUE используется так: в XPath подставляется строка, содержащая недопустимые символы (например, тильду ~), что вызывает ошибку. В текст ошибки попадает переданное значение — это и есть данные, которые хочет получить атакующий.

Функция CONCAT

CONCAT(str1, str2, ...) объединяет строки. В нашем примере она склеивает тильду, результат подзапроса и ещё одну тильду. Тильда здесь — маркер, по которому удобно найти извлечённые данные в сообщении об ошибке.

Функция ELT

ELT(N, str1, str2, ...) возвращает N-ю строку из списка. В примере используется ELT(5401=5401, 1). Поскольку условие 5401=5401 всегда истинно (возвращает 1), ELT вернёт первый элемент — число 1. Это простейший случай; на практике вместо условия может быть подзапрос, извлекающий данные, например: ELT(1=1, (SELECT password FROM users LIMIT 1)).

Пошаговый разбор строки инъекции

Рассмотрим строку по частям:

  • %")) — часть, которая «закрывает» предыдущий SQL-запрос. Символ % часто используется в LIKE-запросах, а ")) завершает кавычки и скобки, делая исходный запрос синтаксически корректным для внедрения.
  • AND EXTRACTVALUE(5401,CONCAT(0x7e,((SELECT (ELT(5401=5401,1)))),0x7e)) — внедряемое условие. Оно всегда истинно (5401=5401), но при выполнении вызывает ошибку, содержащую результат подзапроса.
  • -- - — комментарий, который «отрезает» остаток исходного запроса, чтобы не возникло синтаксических ошибок.

В итоге база данных выполняет подзапрос SELECT (ELT(5401=5401,1)), получает результат (в данном случае 1), подставляет его в CONCAT с тильдами, а затем EXTRACTVALUE пытается обработать строку как XPath и выдаёт ошибку вида: XPATH syntax error: '~1~'. В реальной атаке вместо ELT(5401=5401,1) может стоять подзапрос, возвращающий имя пользователя, хеш пароля и т.д.

Зачем злоумышленнику это нужно?

Цель — получить доступ к данным, которые не должны быть видны. Через error-based инъекцию можно:

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

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

Примеры реальных атак

Подобные строки часто встречаются в логах веб-серверов. Например, при сканировании сайтов утилитами вроде sqlmap можно увидеть множество вариаций с EXTRACTVALUE, UPDATEXML и другими функциями. Если приложение выводит ошибки MySQL на экран, атакующий сразу получает результат.

Вот ещё один пример из практики:

' AND EXTRACTVALUE(3720,CONCAT(0x7e,(SELECT version()),0x7e))-- -

Эта строка вернёт версию MySQL в сообщении об ошибке.

Как защититься от SQL-инъекций?

Главное правило — никогда не доверять данным от пользователя. Вот ключевые меры:

  1. Используйте подготовленные выражения (prepared statements). Это самый надёжный способ. Параметры передаются отдельно от SQL-кода, и внедрение становится невозможным.
  2. Применяйте ORM. Современные ORM (например, Hibernate, Entity Framework) автоматически экранируют параметры.
  3. Валидируйте и фильтруйте входные данные. Если поле должно содержать число — принимайте только числа.
  4. Отключайте вывод ошибок БД на продакшене. Ошибки должны логироваться, но не показываться пользователю.
  5. Используйте Web Application Firewall (WAF). Он может блокировать подозрительные запросы, но не является панацеей.
  6. Регулярно обновляйте СУБД и фреймворки. Уязвимости в старых версиях упрощают атаки.

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

Заключение

Строка %")) AND EXTRACTVALUE(5401,CONCAT(0x7e,((SELECT (ELT(5401=5401,1)))),0x7e))-- - — это типичный пример error-based SQL-инъекции для MySQL. Она демонстрирует, как через функции EXTRACTVALUE, CONCAT и ELT злоумышленник может заставить базу вернуть данные в сообщении об ошибке. Понимание механизма таких атак помогает разработчикам и администраторам лучше защищать свои системы. Помните: лучшая защита — это параметризованные запросы и минимизация вывода ошибок.

Источники