Введение

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

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

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

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

Функция EXTRACTVALUE

EXTRACTVALUE — это встроенная функция MySQL (до версии 8.0), предназначенная для извлечения значения из XML-строки по заданному XPath-выражению. Синтаксис: EXTRACTVALUE(XML_fragment, XPath_expression). Если XPath-выражение некорректно, функция генерирует ошибку, которая включает переданный фрагмент. Эту особенность и используют злоумышленники.

Например, запрос SELECT EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e)) вызовет ошибку вида XPATH syntax error: '~5.7.32~', где 0x7e — это тильда (~), служащая маркером для облегчения чтения результата.

Разбор примера инъекции

Рассмотрим строку: %') AND EXTRACTVALUE(2103,CONCAT(0x7e,((SELECT (ELT(2103=2103,1)))),0x7e))-- -

  • %') — часть, которая закрывает предыдущее выражение в уязвимом запросе. Символ % часто используется в LIKE-запросах, а ') закрывает скобку и кавычку, чтобы вставить свой код.
  • AND EXTRACTVALUE(...) — добавляет условие, которое всегда ложно (или истинно, в зависимости от контекста), но главное — вызывает выполнение функции EXTRACTVALUE.
  • 2103 — произвольное число, первый аргумент EXTRACTVALUE. Он не несёт смысловой нагрузки, но должен быть допустимого типа.
  • CONCAT(0x7e,((SELECT (ELT(2103=2103,1)))),0x7e) — формирует строку для XPath. ELT(2103=2103,1) возвращает 1, так как условие истинно. Это простейший пример; на практике вместо ELT подставляют подзапросы, извлекающие данные (например, имена таблиц, пароли).
  • -- - — комментарий, который обрезает оставшуюся часть оригинального запроса, чтобы синтаксис был корректным.

В результате выполнения такого запроса MySQL вернёт ошибку, содержащую результат подзапроса, обёрнутый в тильды. Например: XPATH syntax error: '~1~'. Злоумышленник, подставляя разные подзапросы, может посимвольно извлекать данные из базы.

Зачем это нужно атакующему?

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

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

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

В учебных целях (например, на платформе DVWA или SQLZoo) часто демонстрируют следующие вариации:

' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT database()), 0x7e))-- -

Этот запрос вернёт имя текущей базы данных. Аналогично можно получить список таблиц:

' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1), 0x7e))-- -

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

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

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

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

Заключение

SQL-инъекция через EXTRACTVALUE — яркий пример того, как ошибки в коде могут привести к утечке данных. Понимание механизма атаки помогает разработчикам осознать важность безопасного программирования. Всегда используйте подготовленные выражения, валидируйте входные данные и следите за обновлениями используемых технологий. Безопасность — это процесс, а не одноразовое действие.

Источники