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

SQL-инъекция — один из самых распространённых способов атаки на веб-приложения, при котором злоумышленник внедряет вредоносный SQL-код в запросы к базе данных. Существует несколько разновидностей: UNION-based, blind, time-based и error-based. Последняя, error-based, основана на том, что СУБД возвращает в сообщении об ошибке фрагмент данных, которые хотел бы получить атакующий. MySQL, в отличие от многих других СУБД, поддерживает функции, специально предназначенные для работы с XML — EXTRACTVALUE и UPDATEXML. Именно они стали инструментами для извлечения информации через ошибки.

Разбор конкретного запроса

Рассмотрим строку:

" OR EXTRACTVALUE(3545,CONCAT(0x7e,((SELECT (ELT(3545=3545,1)))),0x7e))-- -

Эта конструкция — классический пример error-based SQL-инъекции для MySQL. Давайте разберём её по частям.

Начало: " OR ...

Символ двойной кавычки закрывает строковый литерал в оригинальном запросе, после чего оператор OR добавляет новое условие. Если исходный запрос был, например, SELECT * FROM users WHERE name = "$input", то после подстановки получается логическое выражение, которое всегда истинно или вызывает ошибку — в зависимости от цели атакующего.

Функция EXTRACTVALUE

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

Первый аргумент: 3545

Число 3545 — произвольный идентификатор. Оно не несёт смысловой нагрузки, но может использоваться для уникализации запроса или как часть условия 3545=3545, которое всегда истинно. В нашем случае ELT(3545=3545,1) вернёт 1, так как условие истинно.

Функция CONCAT и шестнадцатеричные литералы

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

Подзапрос с ELT

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

Завершение: -- -

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

Как это работает на практике

Предположим, уязвимый запрос выглядит так:

SELECT * FROM articles WHERE id = '$_GET[id]'

Атакующий передаёт в параметр id строку:

1' OR EXTRACTVALUE(3545,CONCAT(0x7e,((SELECT (ELT(3545=3545,1)))),0x7e))-- -

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

SELECT * FROM articles WHERE id = '1' OR EXTRACTVALUE(3545,CONCAT(0x7e,((SELECT (ELT(3545=3545,1)))),0x7e))-- -'

MySQL попытается вычислить EXTRACTVALUE. Второй аргумент — строка ~1~. Это невалидный XPath, поэтому СУБД вернёт ошибку: XPATH syntax error: '~1~'. В этом сообщении атакующий увидит '1' — результат подзапроса. Заменив '1' на подзапрос, например (SELECT version()), он получит версию MySQL, а затем сможет извлекать имена таблиц, колонок и данные.

Почему EXTRACTVALUE опасен?

Функция EXTRACTVALUE доступна в MySQL и MariaDB. Она не требует специальных привилегий, кроме права на выполнение запроса. Ошибка возвращается непосредственно в ответе приложения, если оно не подавляет вывод ошибок. Это делает атаку простой и быстрой: не нужно перебирать символы, как в blind SQL-инъекции, достаточно одного запроса на одно значение.

Меры защиты

  • Параметризованные запросы (prepared statements). Это самый надёжный способ: данные пользователя никогда не попадают в текст SQL-запроса.
  • Экранирование. Если параметризация невозможна, используйте функции экранирования, предоставляемые драйвером БД (например, mysqli_real_escape_string).
  • Отключение вывода ошибок. В production-окружении сообщения об ошибках БД не должны показываться пользователю. Логируйте их, но не выводите.
  • Принцип наименьших привилегий. Учётная запись приложения должна иметь только необходимые права; не используйте root для подключения из кода.
  • WAF и фильтрация. Веб-application firewall может блокировать подозрительные конструкции, но не является панацеей.
  • Регулярные аудиты и сканирование уязвимостей. Используйте инструменты вроде sqlmap для проверки собственных приложений (только в рамках легального тестирования).

Заключение

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

Источники