Защита от SQL-инъекций строится на двух уровнях. В коде — параметризованные запросы, при которых данные пользователя не могут изменить SQL-запрос. На периметре — WAF, который отсекает автоматические атаки до сервера. Нужны оба: код закрывает уязвимость навсегда, а WAF защищает, пока её ещё не нашли.
SQL-инъекция остаётся одной из самых массовых атак на сайты: в OWASP Top 10 2025 инъекции занимают категорию A05. Мы в BotHunt видим это на своём трафике: за 30 дней, с 20 сентября по 19 октября 2026 года, попытки SQL-инъекций пришли на 92% сайтов под защитой нашего WAF. В статье разбираем, как устроены атаки, какие методы защиты реально работают и где у каждого из них граница.
Коротко:
SQL-инъекция — атака, при которой данные из формы или URL становятся частью SQL-запроса и меняют его логику: обходят авторизацию, выгружают или удаляют данные.
Основная защита от SQL-инъекций — параметризованные запросы (prepared statements); экранирование кавычек без них не спасает числовые параметры и сортировку.
По данным BotHunt за 30 дней, 88% попыток SQL-инъекций приходят в теле POST-запроса, поэтому в стандартном access-логе nginx большинство атак не видно.
WAF блокирует типовые и автоматические инъекции и закрывает известные уязвимости CMS виртуальным патчем, но не заменяет исправление кода.
Хороший WAF не мешает живым посетителям: после нашей настройки правил — 0 ложных блокировок на 73 реальных запросах российских сайтов против 5 у стандартного набора.

Что такое SQL-инъекция и почему защита от SQL-инъекций нужна любому сайту
SQL-инъекция (SQL injection, CWE-89) — уязвимость, при которой сайт вставляет ввод пользователя прямо в текст SQL-запроса. Атакующий подбирает ввод так, чтобы он закрыл исходную строку и дописал свою команду. База данных не отличает «запрос программиста» от «дописанного хвоста» и выполняет оба.
Уязвимым бывает любое место, где данные снаружи попадают в базу: поле логина, поиск по каталогу, фильтр товаров, параметр ?id= в адресе, заголовки и cookies, JSON в API. Тип сайта роли не играет: под атаку попадают интернет-магазины, корпоративные сайты на Битриксе и WordPress, личные кабинеты на самописных движках.
Последствия SQL-инъекции тяжелее, чем у большинства веб-атак, потому что атакующий получает прямой доступ к данным:
Утечка данных — выгрузка таблиц клиентов, заказов, хешей паролей. В 2023 году через SQL-инъекцию в MOVEit Transfer (CVE-2023-34362) группировка Cl0p похитила данные сотен организаций.
Обход авторизации — вход в админку без пароля.
Изменение и удаление данных — подмена цен, удаление заказов,
DROP TABLE.Захват сервера — в отдельных конфигурациях СУБД позволяет записать файл или выполнить команду ОС.
Для российского бизнеса утечка — ещё и юридический риск. По 152-ФЗ оператор обязан уведомить Роскомнадзор об утечке персональных данных в течение 24 часов, а с 30 мая 2025 года за утечки действуют повышенные, в том числе оборотные, штрафы.
Как работает SQL-инъекция: пример обхода авторизации
SQL-инъекция срабатывает, когда код склеивает запрос из строк. Типичный уязвимый код проверки логина на PHP выглядит так:
// УЯЗВИМО: ввод пользователя вклеен в SQL
$sql = "SELECT id FROM users WHERE login = '" . $_POST['login'] . "'
AND password_hash = '" . $hash . "'";
$user = $db->query($sql)->fetch();Атакующий вводит в поле логина admin' -- (с пробелом в конце). Запрос превращается в такой:
SELECT id FROM users WHERE login = 'admin' -- ' AND password_hash = '...'Два дефиса в SQL начинают комментарий, поэтому проверка пароля отбрасывается целиком. База возвращает пользователя admin, и атакующий входит без пароля. Вариант ' OR '1'='1 делает условие всегда истинным и возвращает первую запись таблицы — часто это и есть администратор.
Тот же механизм работает в любом месте запроса. В параметре ?id=15 атакующий подставляет 15 UNION SELECT login, password_hash FROM users и получает чужие данные прямо на странице товара. Кавычки здесь даже не нужны: параметр числовой, и экранирование кавычек от него не спасает.
Виды SQL-инъекций и их признаки
SQL-инъекции различаются тем, как атакующий получает результат. Если сайт показывает данные или ошибки — атака занимает секунды. Если нет — атакующий вытаскивает данные по одному биту через логические условия и задержки. Это медленнее, но автоматизируется инструментами вроде sqlmap.
Вид | Как работает | Признак в запросе | Опасность |
|---|---|---|---|
Classic / UNION-based | Дописывает UNION SELECT и выводит чужие данные на страницу |
| Высокая: быстрая выгрузка таблиц |
Error-based | Провоцирует ошибку СУБД, в тексте которой оказываются данные |
| Высокая, если ошибки показываются пользователю |
Blind boolean-based | Задаёт вопросы «да/нет» и смотрит, меняется ли страница |
| Средняя: медленно, но полностью автоматизируется |
Blind time-based | Заставляет базу ждать, если условие истинно |
| Средняя: работает даже без видимого ответа |
Out-of-band | Передаёт данные на сервер атакующего через DNS или HTTP из СУБД |
| Высокая, зависит от настроек СУБД |
Second-order | Вредный ввод сохраняется в базе и срабатывает позже, в другом запросе | Безобидный на входе — опасен при повторном использовании | Высокая: обходит проверку на входе |
Отдельно стоят NoSQL-инъекции — та же идея для MongoDB и подобных баз. Вместо строки атакующий передаёт объект-оператор, например {"$ne": null} в поле пароля, и условие «пароль не равен пустоте» пропускает его в аккаунт. Параметризация здесь означает строгую проверку типа: пароль должен быть строкой, а не объектом.
Что BotHunt видит в трафике: статистика SQL-инъекций за 30 дней
По данным BotHunt, SQL-инъекции получает почти каждый сайт, и большинство атак идёт не через адресную строку, а через тело запроса. Ниже — агрегированная статистика WAF BotHunt за 20 сентября — 19 октября 2026 года. Учитывались только запросы, которые WAF классифицировал как критичные попытки SQL-инъекции. Сайты клиентов в статистике не раскрываются.
Показатель | Значение |
|---|---|
Доля сайтов под WAF BotHunt с попытками SQL-инъекций | 92% |
Критичных попыток SQL-инъекций за 30 дней | 86,8 тыс. запросов |
Уникальных источников атак | 945 |
Доля попыток в теле POST-запроса | 88% |
Доля попыток с российских IP-адресов | около 3% |
Доля попыток из трёх сетей дата-центров | 86% |
Доля попыток от 10 самых активных источников | 73% |
Запросов от источников, уже заблокированных после первых атак | 65% |

Из этих цифр следуют три практических вывода:
Защита от SQL-инъекций нужна даже «неинтересному» сайту. Атакующие не выбирают цель вручную: сканеры перебирают всё подряд, поэтому инъекции получили 92% сайтов, а не только крупные магазины.
Access-лог показывает меньшинство атак. 88% инъекций приходят в теле POST-запроса — в формах, AJAX-обработчиках, API. Стандартный лог nginx тело не пишет, поэтому grep по логу находит лишь около 12% попыток — те, что пришли через GET.
Атаки идут из дата-центров, а не от людей. Три сети хостинга дают 86% попыток, десять источников — 73%. Поэтому блокировка источника после первой инъекции работает: 65% таких запросов пришли от источников, которые WAF BotHunt уже заблокировал после первых атак.
Проверьте, сколько инъекций получает ваш сайт: WAF BotHunt начинает в режиме наблюдения и показывает атаки в кабинете, ничего не блокируя. Подключить облачный WAF →
Главная защита от SQL-инъекций — параметризованные запросы
Параметризованный запрос (prepared statement) отправляет в базу текст SQL и данные раздельно, поэтому данные никогда не исполняются как код. Даже строка admin' -- остаётся просто логином с кавычкой. OWASP ставит этот метод первым среди основных защит в SQL Injection Prevention Cheat Sheet.
Безопасная версия проверки логина на PHP через PDO:
// БЕЗОПАСНО: данные передаются отдельно от SQL
$stmt = $pdo->prepare('SELECT id, password_hash FROM users WHERE login = ?');
$stmt->execute([$_POST['login']]);
$user = $stmt->fetch();
if ($user && password_verify($_POST['password'], $user['password_hash'])) {
// вход разрешён
}Как то же самое делается в популярных стеках:
Стек | Уязвимо | Безопасно |
|---|---|---|
PHP + PDO |
|
|
WordPress |
|
|
1С-Битрикс |
| ORM D7: |
Python |
|
|
Node.js |
|
|
Laravel |
|
|
В Битриксе отдельно стоит проверить старый код и кастомные компоненты. Ядро и ORM D7 передают фильтры безопасно, а уязвимости появляются там, где разработчик вызвал $DB->Query() со склейкой. Если от сырого запроса не уйти, значение нужно пропустить через $helper->forSql() и привести числа к int. О других слабых местах Битрикса — в статье про защиту админки Bitrix от взлома.
Где параметры не спасают: ORM, сортировка и second-order
Параметризовать можно только значения, но не структуру запроса — имена таблиц, колонок и направление сортировки. Именно здесь чаще всего остаются инъекции в коде, который в целом написан правильно.
Динамическая сортировка.
ORDER BY ?не работает: плейсхолдер подставит строку, а не имя колонки. Поле сортировки нужно брать из белого списка, а не из запроса.Сырые запросы в ORM.
raw(),whereRaw(),extra(),$DB->Query()— ORM здесь только передаёт строку дальше. Склейка внутри них так же опасна, как без ORM.Списки IN и поиск LIKE. Для
IN (...)генерируйте нужное число плейсхолдеров. ВLIKEэкранируйте%и_, иначе пользователь управляет шаблоном поиска.Second-order. Значение безопасно сохранили через prepared statement, а потом другой модуль — отчёт, выгрузка, крон — достал его из базы и вклеил в новый запрос. Правило простое: данные из базы тоже недоверенные.
Пример безопасной сортировки через белый список:
$allowed = ['price' => 'price', 'date' => 'created_at', 'name' => 'title'];
$column = $allowed[$_GET['sort'] ?? ''] ?? 'created_at';
$dir = (($_GET['dir'] ?? '') === 'asc') ? 'ASC' : 'DESC';
$stmt = $pdo->prepare("SELECT * FROM products WHERE category_id = ? ORDER BY $column $dir");
$stmt->execute([(int) $_GET['cat']]);Дополнительные уровни защиты от SQL-инъекций
Дополнительные меры не заменяют параметризацию, но уменьшают ущерб, если инъекция всё же найдётся. Это принцип эшелонированной защиты: каждый слой отнимает у атакующего часть возможностей.
Минимальные права учётной записи БД. Сайту не нужны права
DROP,FILE,GRANTи доступ к чужим базам. Отдельный пользователь только сSELECT/INSERT/UPDATE/DELETEна свою базу превращает потенциальный захват сервера в ограниченную утечку.Скрытые ошибки СУБД. Текст SQL-ошибки на странице — подарок для error-based атаки. В продакшене ошибки пишутся в лог, пользователю показывается нейтральная страница.
Валидация по белому списку. Число приводится к
int, дата — к формату даты, значение из выпадающего списка сверяется с допустимыми. Это не защита от инъекций, а отсев мусора до запроса.Экранирование — только для legacy.
mysqli_real_escape_string()защищает строку в кавычках при правильной кодировке соединения, но не числовые параметры и не имена колонок.Обновления CMS и модулей. Многие инъекции живут не в вашем коде, а в сторонних плагинах. Обновление закрывает известные дыры, а WAF прикрывает время до обновления.
Как облачный WAF защищает от SQL-инъекций
WAF (Web Application Firewall) проверяет каждый HTTP-запрос до того, как он дойдёт до сайта, и блокирует запросы с признаками атаки. Если параметризованные запросы — это исправление причины, то WAF — фильтр на входе: он перехватывает массовые автоматические инъекции и даёт время исправить код. Подробнее о самой технологии — в статье «Что такое WAF».
Облачный WAF BotHunt работает на собственных узлах в России. Сайт подключается через DNS: трафик идёт через узлы BotHunt, а на сервер доходят только чистые запросы. Каждый запрос проходит несколько проверок:
Сигнатуры OWASP Core Rule Set — открытый набор правил для типовых атак: UNION SELECT, логические условия, функции задержки, обращения к системным таблицам.
Грамматический разбор libinjection — распознаёт SQL-синтаксис в параметре, даже если ключевые слова записаны непривычно.
Проверка тела запроса, а не только URL. Как показывает наша статистика, именно там 88% атак.
Проверка ответа сервера — если в ответ всё же просочилась SQL-ошибка, это видно и попадает в журнал как признак уязвимости.
Блокировка источника — после критичной инъекции источник блокируется, и его следующие запросы отсекаются сразу.

Сильная сторона WAF — виртуальный патчинг. Когда в популярном модуле находят уязвимость, обновление выходит не сразу, а владельцы сайтов ставят его ещё позже. WAF закрывает такую дыру правилом на периметре за минуты. Так мы закрыли уязвимости удалённого выполнения кода в модулях Битрикса и PHP-CGI (CVE-2024-4577) и следим за каталогом активно эксплуатируемых уязвимостей CISA KEV.
Граница применимости тоже важна. Ни один WAF не распознаёт все обфусцированные инъекции: атакующий, который целенаправленно работает с одним сайтом, со временем найдёт вариант записи, которого нет в правилах. Поэтому WAF не отменяет исправление кода, а закрывает массовые атаки и выигрывает время. Чем WAF отличается от защиты от ботов и почему их стоит сочетать — в статье «WAF vs антибот».
WAF входит в тарифы BotHunt от 890 ₽/мес: подключение через DNS за 5 минут, без установки модулей на сервер. Посмотреть, как работает WAF BotHunt →
Ложные срабатывания: почему WAF блокирует обычные формы
Ложное срабатывание — это блокировка настоящего посетителя, чей запрос похож на атаку. Для защиты от SQL-инъекций это главная практическая проблема: в обычных данных постоянно встречаются кавычки, слова select и union, спецсимволы.
Типичные источники ложных блокировок на российских сайтах:
Обмен с 1С — выгрузки каталога и заказов передают большие XML и параметры, похожие на код.
GraphQL и JSON API — фигурные скобки, кавычки и ключевые слова в теле запроса.
Тексты пользователей — отзыв «лучший выбор, select из всех вариантов» или фамилия с апострофом вроде O'Connor.
Визуальные редакторы админки — HTML-разметка и скрипты в поле контента у администратора.
Мы проверили это на стенде. Стандартный набор правил OWASP CRS на 73 реальных запросах российских сайтов дал 5 ложных блокировок — на обмене с 1С, GraphQL и PUT-загрузках. После нашей настройки правил — 0 из 73 при всех пойманных атаках тестового корпуса. На 141 безобидном тексте открытого корпуса gotestwaf наш WAF тоже не дал ни одной ложной блокировки.
Второй механизм — режим наблюдения. Новый сайт в BotHunt стартует в режиме, когда WAF видит атаки, но не блокирует их. За это время он изучает, как работает сайт, и автоматически добавляет исключения для легитимных сценариев вроде обмена с 1С. Блокировка включается, когда ложных срабатываний не остаётся.
Облачный WAF, ModSecurity на сервере или только код
Для большинства сайтов оптимально сочетание: параметризованные запросы в коде и облачный WAF перед сайтом. ModSecurity на своём сервере даёт контроль, но требует администратора и ресурсов. Сравнение по практическим критериям:
Критерий | Только исправление кода | ModSecurity на сервере | Облачный WAF |
|---|---|---|---|
Защищает от инъекций в вашем коде | Да, навсегда | Частично | Частично |
Прикрывает уязвимости в плагинах до обновления | Нет | Если правила обновляются | Да, виртуальный патч |
Нагрузка на ваш сервер | Нет | Да, проверка каждого запроса | Нет, проверка на узлах |
Работает на виртуальном хостинге | Да | Обычно нет | Да, через DNS |
Кто обновляет правила | — | Ваш администратор | Провайдер WAF |
Настройка ложных срабатываний | — | Вручную | Режим наблюдения и автоматические исключения |
Отсекает сканеры до сервера | Нет | На сервере | Да |
Как проверить сайт на SQL-инъекции
Проверять можно только свой сайт или сайт, на который есть письменное разрешение владельца. Несанкционированный поиск уязвимостей на чужих ресурсах может подпадать под статью 272 УК РФ.
Ручная проверка. Добавьте одиночную кавычку к параметру:
/product?id=15'. Если в ответ пришла ошибка базы данных или страница сломалась — параметр почти наверняка уязвим.Статический анализ кода. Инструменты вроде Semgrep находят склейку SQL-строк с пользовательским вводом прямо в репозитории — ещё до выкладки.
Автоматический сканер на тестовой копии. sqlmap проверяет параметр всеми видами инъекций, включая blind и time-based. Запускайте его на копии сайта, а не на рабочей базе.
Журнал атак WAF. Если сайт уже защищён WAF, журнал покажет, какие страницы и параметры атакуют. Это готовая карта мест, которые стоит проверить в коде.
Если WAF ещё нет, часть атак видно в логах веб-сервера. Команда ниже находит типовые признаки инъекций в access-логе nginx. Помните про ограничение: в логе только URL, а большинство атак приходит в теле POST. Больше команд для разбора логов — в статье «Поиск ботов в логах Nginx».
grep -iE "union(%20|\+|%2b|\s)+select|information_schema|sleep(%28|\()|benchmark(%28|\()|%27(%20|\+)*or(%20|\+)" \
/var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | headЧто делать, если сайт взломали через SQL-инъекцию
Сначала закройте уязвимость, затем выясните масштаб и выполните требования закона. Порядок действий:
Закройте вектор атаки. Исправьте уязвимый код или временно закройте страницу. Если исправление займёт время, включите WAF с блокировкой — виртуальный патч остановит повторные попытки.
Смените все секреты. Пароль пользователя БД, пароли администраторов CMS, ключи API и платёжных систем, соли и ключи сессий из конфигурации.
Проверьте базу и файлы. Новые администраторы, изменённые email, посторонний JavaScript в контенте и шаблонах, неизвестные PHP-файлы.
Определите объём утечки. Логи веб-сервера, журнал WAF и логи СУБД покажут, какие таблицы читали.
Выполните требования 152-ФЗ. При утечке персональных данных уведомите Роскомнадзор в течение 24 часов, а о результатах расследования — в течение 72 часов.
Восстановите и наблюдайте. Восстановление из чистой резервной копии, затем повышенное внимание к журналу атак: атакующие часто возвращаются по известной дыре.
Чек-лист: защита от SQL-инъекций для сайта
Все запросы с пользовательскими данными — через prepared statements или штатные методы ORM.
Имена колонок, таблиц и направление сортировки — только из белого списка.
В коде нет склейки SQL в raw-запросах,
$DB->Query(),whereRaw()и аналогах.Данные, прочитанные из базы, при повторном использовании тоже параметризуются.
Учётная запись БД сайта имеет только необходимые права.
Ошибки СУБД не выводятся пользователю.
CMS и модули обновлены, неиспользуемые плагины удалены.
Перед сайтом стоит WAF, который проверяет тело запроса, а не только URL.
Есть резервные копии и план действий на случай утечки.
Подключите облачный WAF BotHunt — он закроет массовые SQL-инъекции, пока вы проверяете код. Начать бесплатно →
Часто задаваемые вопросы
Как защититься от SQL-инъекций на сайте?
Главная защита от SQL-инъекций — параметризованные запросы (prepared statements): данные пользователя передаются в базу отдельно от текста SQL и не могут изменить его смысл. Вторым рубежом служат минимальные права учётной записи БД, скрытые ошибки базы и WAF, который отсекает массовые автоматические атаки до сервера.
Защитит ли WAF от SQL-инъекций без исправления кода?
WAF блокирует типовые инъекции — UNION SELECT, логические и временные blind-атаки, шаблоны sqlmap — и закрывает известные уязвимости CMS виртуальным патчем. Но ни один WAF не распознаёт все обфусцированные варианты, поэтому уязвимый код всё равно нужно исправить: WAF выигрывает время, а не заменяет prepared statements.
Достаточно ли экранировать кавычки, чтобы защититься от SQL-инъекций?
Нет. Экранирование вроде addslashes() или mysqli_real_escape_string() не защищает числовые параметры без кавычек, имена колонок в ORDER BY и запросы с неверно выставленной кодировкой соединения. Экранирование — запасной вариант для legacy-кода, основным методом должны быть параметризованные запросы.
Почему инъекции не видны в логах nginx?
По нашим данным, 88% попыток SQL-инъекций приходят в теле POST-запроса, а стандартный access-лог nginx тело запроса не записывает. В логе вы видите только адрес и код ответа, поэтому поиск grep по UNION SELECT находит лишь меньшую часть атак — их видно в журнале WAF, который разбирает тело.
Защищает ли ORM от SQL-инъекций?
ORM защищает, пока вы пользуетесь его штатными методами: фильтры и параметры он передаёт в базу безопасно. Уязвимость возвращается, когда в ORM пишут сырой SQL со склейкой строк — например, через raw-запросы, whereRaw() в Laravel, $DB->Query() в Битриксе или динамическую сортировку по полю из запроса.
Что делать, если сайт уже взломали через SQL-инъекцию?
Закройте уязвимость (исправление кода или виртуальный патч WAF), смените пароли БД, администраторов и ключи API, проверьте базу на добавленных администраторов и вредоносный код. Если утекли персональные данные, по 152-ФЗ нужно уведомить Роскомнадзор в течение 24 часов, а о результатах расследования — в течение 72 часов.
Как проверить свой сайт на SQL-инъекции?
На своём сайте начните с ручной проверки: одиночная кавычка в параметре не должна вызывать ошибку базы данных. Затем подключите статический анализ кода (например, Semgrep) и проверку sqlmap на тестовой копии. Сканировать чужие сайты без письменного разрешения владельца нельзя — это уголовно наказуемо.






