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

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

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

Функция EXTRACTVALUE в MySQL

Функция EXTRACTVALUE(XML_frag, XPath_expr) извлекает значение из фрагмента XML по заданному XPath-выражению. Если XPath-выражение некорректно или не соответствует синтаксису, MySQL возвращает ошибку XPATH syntax error. Эта ошибка содержит часть переданного XPath-выражения, что и позволяет злоумышленнику извлекать данные.

Пример корректного использования:

SELECT EXTRACTVALUE('<a><b>Текст</b></a>', '/a/b');

Результат: Текст.

Однако если передать в XPath что-то вроде ~текст~, MySQL выдаст ошибку, в тексте которой будет фигурировать этот текст. Именно на этом строится атака.

Разбор запроса: ' OR EXTRACTVALUE(4752,CONCAT(0x7e,((SELECT (ELT(4752=4752,1)))),0x7e))-- -

Рассмотрим по частям, что делает этот запрос.

  • ' — закрывает строковый литерал в исходном запросе приложения. Предполагается, что введённые данные подставляются в SQL-запрос внутри кавычек.
  • OR — логический оператор, который делает условие всегда истинным (если правая часть истинна) или позволяет внедрить дополнительное выражение.
  • EXTRACTVALUE(4752, CONCAT(0x7e, ((SELECT (ELT(4752=4752,1)))), 0x7e)) — вызов функции, который должен вызвать ошибку с внедрёнными данными.
  • -- - — комментарий, обрезающий остаток оригинального запроса.

Внутри EXTRACTVALUE используется CONCAT(0x7e, ..., 0x7e). 0x7e — это шестнадцатеричное представление символа ~ (тильда). Тильда добавляется, чтобы отделить извлекаемые данные от служебного текста ошибки.

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

В результате MySQL попытается вычислить XPath-выражение ~1~, что приведёт к ошибке вида:

ERROR 1105 (HY000): XPATH syntax error: '~1~'

Если вместо единицы подставить подзапрос, в ошибке отобразится результат этого подзапроса — например, имя пользователя или хеш пароля.

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

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

Типичный сценарий атаки:

  1. Найти уязвимый параметр (например, ?id=1').
  2. Внедрить конструкцию с EXTRACTVALUE и подзапросом для получения имени текущей базы данных: EXTRACTVALUE(1, CONCAT(0x7e, (SELECT database()), 0x7e)).
  3. Получить в ошибке имя базы, затем таблицы, столбцы и сами данные.

Примеры полезных подзапросов

  • Версия MySQL: SELECT version()
  • Текущий пользователь: SELECT current_user()
  • Имя базы данных: SELECT database()
  • Список таблиц: SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1
  • Данные из конкретной таблицы: SELECT password FROM users LIMIT 1

Все эти подзапросы можно вставить вместо ELT(4752=4752,1) в приведённом запросе.

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

Основные меры защиты:

  • Параметризованные запросы (prepared statements) — самый надёжный способ. Данные передаются отдельно от SQL-кода и не интерпретируются как часть запроса.
  • Экранирование специальных символов — если параметризация невозможна, используйте функции экранирования, предоставляемые драйвером БД.
  • Отключение вывода ошибок БД — не показывайте пользователям сообщения об ошибках SQL. Логируйте их, но не выводите в браузер.
  • Ограничение прав пользователя БД — приложение должно работать с минимально необходимыми привилегиями (обычно только SELECT, INSERT, UPDATE, DELETE для конкретных таблиц).
  • WAF (Web Application Firewall) — может блокировать известные шаблоны атак, но не является панацеей.

Заключение

Запрос ' OR EXTRACTVALUE(4752,CONCAT(0x7e,((SELECT (ELT(4752=4752,1)))),0x7e))-- - — это учебный пример error-based SQL-инъекции в MySQL. Он демонстрирует, как через функцию EXTRACTVALUE и намеренно вызванную ошибку XPath можно извлечь произвольные данные из базы. Понимание таких техник необходимо как специалистам по безопасности, так и разработчикам для написания защищённого кода.

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

Источники