Механизм работы уязвимости
Любая база данных ожидает, что поступающий текст состоит из инструкций языка SQL и параметров. Инструкции объясняют серверу, какое действие предпринять: выбрать строки, обновить баланс, удалить запись. Параметры выступают содержимым: логин пользователя, номер заказа или дата рождения. В уязвимых приложениях разработчики склеивают команды и входные данные в единую текстовую строку перед отправкой в базу данных.
СУБД получает готовый текст и передает его внутреннему парсеру. Парсер разбирает лексемы слева направо. Ему неизвестно, какую часть текста написал разработчик на бэкенде, а какую вписал случайный посетитель сайта в поле авторизации. Одинарные кавычки, точки с запятой и дефисы меняют синтаксическое дерево. Пользовательский текст перестает быть пассивным значением и начинает исполняться как управляющий код.
Когда синтаксис команды искажен, СУБД добросовестно выполняет результат перестроения. Инъекция может заставить сервер обойти аутентификацию, выгрузить полную структуру таблиц, прочитать локальные файлы операционной системы или записать исполняемый скрипт в общедоступную веб-директорию. Реакция базы зависит от прав пользователя, под которым сервер подключается к хранилищу.
Анатомия классического взлома авторизации
Представим форму входа на сайт, где бэкенд на PHP, Python или Java принимает имя пользователя и пароль. Неопытный программист формирует запрос так: объединяет команду SELECT с переменной, пришедшей из формы. Запрос должен проверить совпадение логина и хеша пароля в таблице users.
Второй распространенный вариант атаки эксплуатирует логические операторы. В поле ввода отправляют конструкцию вроде ' OR '1'='1. Запрос превращается в выражение, истинное для абсолютно каждой строки таблицы. В результате СУБД возвращает серверу первую попавшуюся запись, которой часто оказывается учетная запись администратора, созданная при установке базы первой.
Разновидности SQL-инъекций
Инъекции разделяют по каналу получения информации и поведению сервера в ответ на вредоносную полезную нагрузку.
- Классическая инъекция через объединение использует оператор UNION для слияния легитимной выборки с данными из системных таблиц СУБД прямо на экране сайта.
- Инъекция на основе ошибок намеренно передает СУБД некорректные математические функции или преобразования типов, чтобы сервер вернул технический текст ошибки с фрагментами конфиденциальных данных.
- Слепая инъекция на логических условиях заставляет страницу слегка менять структуру, возвращая ответ True или False, по крупицам раскрывая символы паролей через бинарный поиск.
- Слепая инъекция по времени принуждает СУБД выполнять функцию задержки вроде SLEEP или WAITFOR DELAY, сигнализируя о правильности угаданного байта паузой в ответе сервера.
- Второстепенная инъекция сохраняет вредоносный код в базе данных при регистрации или создании тикета, а срабатывает позже, когда сохраненный текст повторно подставляется в другой уязвимый запрос административной панели.
Громкие инциденты в истории безопасности
В 2008 году хакер Альберт Гонсалес и его сообщники применили SQL-инъекцию против платежного шлюза Heartland Payment Systems. Через уязвимость на корпоративном сайте они внедрили анализатор трафика и похитили информацию о 130 миллионах банковских карт. Финансовые потери компании на компенсации банкам, штрафы и судебные издержки превысили 140 миллионов долларов.
В 2011 году хакерская группировка LulzSec взломала инфраструктуру подразделения Sony Pictures. Атакующие использовали тривиальную слепую SQL-инъекцию, о чем публично написали после публикации украденного архива. Из-за отсутствия базовой параметризации в открытый доступ попали пароли, электронные адреса и персональные данные одного миллиона пользователей. Серверы компании хранили эти сведения в открытом виде.
Надежные методы защиты
Главный и фундаментальный инструмент защиты — параметризованные запросы, также известные как подготовленные выражения (Prepared Statements). При их использовании взаимодействие с СУБД разбивается на строго разделенные этапы.
Сначала веб-приложение отправляет в базу каркас SQL-запроса, в котором на месте переменных данных стоят вопросительные знаки или именованные плейсхолдеры. База данных компилирует этот запрос, строит дерево синтаксического анализа и план выполнения. Текст запроса зафиксирован, изменить его синтаксическую структуру уже невозможно.
Затем отдельным сетевым пакетом сервер передает сами значения параметров. СУБД берет эти значения и вставляет в предварительно скомпилированные слоты исключительно как литералы. Если злоумышленник передает кавычки, дефисы или операторы UNION, они воспринимаются базой просто как символы в строке имени, а не как команды.
Помимо подготовленных выражений в разработке применяют эшелонированную оборону. Она сдерживает последствия атаки, если программист где-то допустил оплошность.
- Использование современных объектно-реляционных отображений вроде Hibernate, Entity Framework или SQLAlchemy, которые генерируют параметризованные конструкции автоматически.
- Соблюдение принципа наименьших привилегий, при котором учетная запись веб-приложения в базе данных лишена прав на удаление таблиц DROP TABLE, чтение системного каталога и доступ к командной оболочке ОС.
- Хранимые процедуры базы данных, изолирующие бизнес-логику и запрещающие прямой доступ клиентских сервисов к базовым таблицам.
- Белые списки для структурных элементов запроса, если имена таблиц или колонок сортировки нельзя передать через стандартные параметры.
- Межсетевые экраны веб-приложений класса WAF, блокирующие типовые сигнатуры атак на сетевом периметре.
Частые вопросы
Спасает ли использование ORM от SQL-инъекций на сто процентов?
Большинство ORM по умолчанию генерируют безопасные параметризованные запросы. Уязвимость возникает, когда разработчик намеренно вызывает методы выполнения сырого SQL-кода вроде raw() или query() и вручную вклеивает туда данные через форматирование строк.
Можно ли взломать базу данных через поля для ввода чисел?
Да, числовые поля наиболее опасны при отсутствии параметризации. В выражении WHERE id = $id кавычек нет изначально, поэтому злоумышленнику даже не нужно закрывать строковый литерал — достаточно передать после цифры пробел и вредоносную конструкцию UNION SELECT.
Почему экранирование спецсимволов считается устаревшим подходом?
Функции экранирования зависят от текущей кодировки соединения между веб-сервером и СУБД. При несовпадении таблиц символов атакующий может применить многобайтовые последовательности, нейтрализующие обратный слеш, что открывает путь для внедрения кавычки.
Чем опасна инъекция второго порядка?
Она сложнее для обнаружения. На первом этапе приложение корректно сохраняет строку в базе, но на втором этапе извлекает эту строку и подставляет ее без параметров во внутренний вспомогательный запрос, вызывая выполнение полезной нагрузки.