Что такое SQL-инъекция и как защитить от неё сайт
SQL-инъекция — это атака на сайт, при которой злоумышленник подсовывает в форму, адресную строку или cookie фрагмент SQL-кода, а сайт выполняет его в базе данных как собственную команду. В результате посторонний человек может прочитать таблицу клиентов, войти в админку без пароля или стереть заказы. Если коротко, SQL-инъекции — это следствие одной ошибки разработчика: пользовательские данные склеиваются с текстом запроса. Ниже разберём, что такое SQL-инъекция на простом примере, какие бывают виды, чем утечка базы грозит бизнесу по 152-ФЗ, как проверить свой сайт и как защититься. Статья для владельца сайта и его разработчика: владельцу хватит текста и таблиц, разработчику пригодятся примеры кода.
Что такое SQL-инъекция простыми словами
Почти любой сайт с каталогом, личным кабинетом или формой заявки хранит данные в базе: MySQL, PostgreSQL, MS SQL. Сайт общается с базой на языке SQL: «найди товар с id 15», «проверь логин и пароль», «сохрани заказ». Часть такого запроса берётся из того, что прислал посетитель: номер товара из ссылки, текст из поиска, логин из формы входа.
Представьте бланк, в котором сотрудник вписывает фамилию клиента в готовую фразу «Выдать справку клиенту ___» и не глядя передаёт в архив. Если клиент впишет вместо фамилии «Иванову, а также выдать все личные дела», архив выполнит и вторую часть. SQL-инъекция работает так же: база не отличает, где заканчиваются данные посетителя и начинается команда программиста, потому что всё пришло одной строкой.
Как работает SQL-инъекция: пример уязвимого и исправленного кода
Разберём учебный пример на PHP — языке, на котором написаны WordPress и 1С-Битрикс. Он показывает сам механизм, а не приём атаки на конкретный сайт.
Уязвимый запрос: склейка строк
// Карточка товара: /product.php?id=15
$id = $_GET['id'];
$sql = "SELECT name, price FROM products WHERE id = " . $id;
$result = $db->query($sql);
Пока в ссылке число, всё работает. Но значение id целиком контролирует посетитель. Если вместо «15» передать «15 OR 1=1», база получит запрос WHERE id = 15 OR 1=1, условие станет истинным для всех строк, и страница выдаст весь каталог. Это безобидная демонстрация, но тем же путём в запрос можно дописать чтение другой таблицы, например пользователей с хешами паролей. Классический пример с формой входа — ввод ' OR '1'='1 в поле пароля: если проверка собрана склейкой, условие «пароль совпал» всегда истинно.
Исправленный запрос: PDO и prepared statements
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
]);
$id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT);
if ($id === false || $id === null) {
http_response_code(404);
exit;
}
$stmt = $pdo->prepare('SELECT name, price FROM products WHERE id = :id');
$stmt->execute(['id' => $id]);
$product = $stmt->fetch();
Здесь текст запроса отправляется в базу отдельно от значения. База сначала разбирает шаблон с меткой :id, а потом подставляет данные строго как значение. Что бы ни пришло в параметре, командой оно уже не станет. Дополнительно число проверяется на входе: мусор отсекается ещё до обращения к базе.
ORDER BY. Для таких мест используйте белый список: сравнивайте значение с перечнем разрешённых столбцов и подставляйте только значение из этого перечня.Виды SQL-инъекций: классическая, слепая, time-based, second-order
Типы SQL-инъекций различаются тем, как атакующий получает результат. Для владельца важно одно: «невидимые» виды не безопаснее видимых, просто данные вытягиваются медленнее.
| Вид | Как работает | Чем опасна | Как заметить |
|---|---|---|---|
| Классическая (in-band: UNION, error-based) | Результат чужого запроса выводится прямо на страницу или в текст ошибки базы | Быстрое выкачивание таблиц целиком | Ошибки SQL на страницах, странные параметры в логах |
| Слепая (boolean-based) | Страница не показывает данные, но меняется в зависимости от того, истинно ли условие. Данные вытягиваются по одному биту | Работает даже при скрытых ошибках | Тысячи однотипных запросов к одному адресу |
| По времени (time-based) | Атакующий заставляет базу «задуматься» на несколько секунд, если условие верно, и судит по задержке ответа | Обходит сайты, где ответ всегда одинаковый | Всплески медленных запросов, нагрузка на базу |
| Отложенная (second-order) | Вредная строка сначала безопасно сохраняется, например в имени профиля, а срабатывает позже, когда другой участок кода вставляет её в запрос склейкой | Проходит мимо проверки форм и простых сканеров | Только анализ кода |
| Внеполосная (out-of-band) | База сама отправляет данные на внешний сервер атакующего, например через DNS-запрос | Редкая, зависит от настроек СУБД | Исходящие соединения с сервера базы |
Чем SQL-инъекция опасна для бизнеса
Одна уязвимая строка кода даёт доступ ко всей базе, с которой работает сайт. На практике это означает:
- Утечку клиентской базы: ФИО, телефоны, адреса доставки, история заказов. Такие дампы продаются и потом используются для мошеннических звонков от имени вашей компании.
- Захват админки: из таблицы пользователей достаются логины и хеши паролей, слабые хеши подбираются, дальше меняются реквизиты оплаты, подкладываются вредоносные скрипты.
- Порчу и удаление данных: изменённые цены, стёртые заказы, подменённые страницы.
- Плацдарм для дальнейшего взлома: при лишних правах у пользователя базы можно записать файл на сервер и получить полноценный доступ к хостингу.
Утечка через SQL-инъекцию и 152-ФЗ
Если через уязвимость утекли персональные данные, отвечает оператор, то есть владелец сайта, а не хакер. По 152-ФЗ о неправомерной передаче персональных данных нужно уведомить Роскомнадзор дважды: в течение 24 часов с момента выявления — о самом инциденте, предполагаемых причинах и принятых мерах, в течение 72 часов — о результатах внутреннего расследования и виновных, если они установлены. С 30 мая 2025 года действуют повышенные штрафы по ст. 13.11 КоАП РФ для компаний и ИП:
| Нарушение | Штраф для компании / ИП |
|---|---|
| Не уведомили Роскомнадзор об утечке | 1–3 млн ₽ |
| Утечка данных 1 000–10 000 человек | 3–5 млн ₽ |
| Утечка данных 10 000–100 000 человек | 5–10 млн ₽ |
| Утечка данных более 100 000 человек | 10–15 млн ₽ |
| Утечка специальных категорий данных | 10–15 млн ₽ |
| Утечка биометрических данных | 15–20 млн ₽ |
| Повторная утечка | 1–3% годовой выручки, но не меньше 20 млн ₽ (для специальных категорий и биометрии — 25 млн ₽) и не больше 500 млн ₽ |
Где чаще всего встречаются SQL-инъекции
Современные фреймворки и ядра популярных CMS по умолчанию защищены: в Laravel, Symfony, Django, Prisma и ORM 1С-Битрикс (D7) запросы строятся с параметрами. Инъекции появляются там, где разработчик обошёл эти механизмы или их не было:
- Самописные сайты 2000-х и 2010-х на «голом» PHP с устаревшим расширением mysql_* и ручной склейкой запросов.
- Плагины и модули сторонних авторов для WordPress, Битрикс, OpenCart. Уязвимости в ядре редки, в дополнениях их находят регулярно, а обновления ставят не все.
- Легаси-доработки: фильтры каталога, выгрузки, отчёты, интеграции с 1С, написанные на скорую руку в обход ORM, например через
$wpdb->query()без$wpdb->prepare()или$DB->Query()в Битрикс без экранирования. - Сырые запросы внутри ORM:
DB::raw(),$queryRawUnsafeи аналоги, куда подставлен ввод пользователя. - Сортировка, поиск и фильтры: параметры вроде
sort=priceилиorder=desc, которые нельзя передать параметром и которые часто вставляют как есть.
Если сайт делали несколько подрядчиков и никто не знает, что лежит в коде, риск выше среднего. Об общих угрозах для сайтов бизнеса подробнее в статье о кибербезопасности сайта в 2026 году.
SQL-инъекция в OWASP Top 10 2025
OWASP Top 10 — самый известный рейтинг рисков веб-приложений, на него ссылаются аудиторы, банки-эквайеры и требования к разработке. В редакции 2025 года категория Injection занимает пятое место (A05:2025). В 2017 году она была первой, в 2021-м — третьей. Снижение говорит не о том, что проблема ушла, а о том, что фреймворки с параметризованными запросами стали нормой.
При этом по данным OWASP на инъекции проверяли 100% протестированных приложений, а у категории больше всего известных уязвимостей (CVE): свыше 14 тысяч приходится именно на SQL-инъекции. Для старого кода и сторонних модулей это по-прежнему одна из самых реальных угроз.
Как проверить сайт на SQL-инъекции
Самостоятельно владелец может проверить косвенные признаки, а полноценная проверка — работа специалиста. Методы дополняют друг друга:
- Анализ исходного кода (ручной и SAST). Поиск мест, где ввод пользователя попадает в запрос склейкой. Единственный способ найти second-order инъекции и уязвимости на закрытых страницах.
- Динамическое сканирование (DAST). OWASP ZAP, Burp Suite и аналоги отправляют тестовые данные в формы и параметры и анализируют ответы. Находят очевидное, дают ложные срабатывания, которые нужно проверять вручную.
- Специализированные инструменты вроде sqlmap. Подтверждают, эксплуатируется ли уязвимость. Запускать их можно только на своём сайте или с письменного разрешения владельца, лучше на копии: тесты создают нагрузку и могут изменить данные. Атака на чужой сайт, даже «проверочная», может быть квалифицирована как неправомерный доступ к компьютерной информации (ст. 272 УК РФ).
- Логи веб-сервера и WAF. Параметры с кавычками, словами UNION, SELECT, SLEEP, тысячи запросов к одной странице с одного адреса — признак, что вас уже пробуют.
- Проверка версий CMS и модулей по базам известных уязвимостей: если у установленной версии плагина есть опубликованная SQL-инъекция, её найдут автоматические боты раньше вас.
Базовую самопроверку сайта без инструментов пентестера можно пройти по чек-листу из 15 пунктов. Если нужен полный разбор кода, конфигурации и прав с отчётом по критичности, это задача для аудита безопасности сайта.
Как защититься от SQL-инъекций
Методы защиты работают слоями. Первый пункт закрывает саму уязвимость, остальные снижают ущерб, если где-то осталась ошибка.
- Параметризованные запросы или ORM везде. PDO и mysqli с prepared statements в PHP,
$wpdb->prepare()в WordPress, ORM D7 в Битрикс, query builder в Laravel, параметры в node-postgres и mysql2. Сырой SQL со склейкой — только с обоснованием на код-ревью. - Белые списки для идентификаторов. Имена столбцов, направление сортировки, названия таблиц выбираются из заранее заданного перечня.
- Валидация входных данных. Число должно быть числом, дата — датой, длина строки ограничена. Это не замена параметрам, а второй барьер.
- Минимальные права пользователя базы. Сайту не нужны права на удаление таблиц, запись файлов (FILE в MySQL) и доступ к чужим базам. Отдельные учётки для сайта, админки и выгрузок.
- Скрытие ошибок на продакшене. Текст ошибок SQL пишется в лог, посетитель видит нейтральную страницу. Это не устраняет уязвимость, но лишает атакующего подсказок.
- WAF. Веб-фаервол на уровне хостинга, nginx (ModSecurity) или облачного сервиса блокирует типовые шаблоны атак и выигрывает время до исправления кода.
- Обновления и контроль зависимостей. Ядро CMS, плагины, модули, пакеты npm и Composer обновляются регулярно, заброшенные расширения заменяются.
- Надёжное хранение паролей и бэкапы. Пароли только в виде хешей bcrypt или Argon2, резервные копии базы вне сервера сайта.
Типичные ошибки в защите от SQL-инъекций
- Ручное экранирование вместо параметров.
addslashes()и самописные функции чистки ломаются на кодировках и числовых параметрах без кавычек. - Чёрный список слов. Фильтр, вырезающий SELECT и UNION, обходится сменой регистра, комментариями и кодированием.
- Ставка только на WAF. Фаервол не видит отложенные инъекции и пропускает нестандартные обходы. Это страховка, а не лечение.
- Проверка только в браузере. JavaScript-валидация формы отключается за секунду, запрос можно отправить напрямую.
- Защищены формы, но не API и выгрузки. Мобильное приложение, обмен с 1С и внутренние отчёты тоже принимают данные.
Что делать, если сайт взломали через SQL-инъекцию
- Сохраните логи веб-сервера, базы и WAF за последние недели до любых чисток: без них не установить, что и когда утекло.
- Закройте вход: отключите уязвимый раздел или временно переведите сайт в режим обслуживания, смените пароли к базе, админке, FTP и SSH, ключи API.
- Оцените, были ли затронуты персональные данные. Если да — уведомление в Роскомнадзор в течение 24 часов, результаты расследования в течение 72 часов.
- Найдите и исправьте уязвимый код, а не только удалите последствия. Проверьте, не оставлены ли на сервере веб-шеллы и новые учётки администраторов.
- Проверьте целостность данных: цены, реквизиты оплаты, настройки почты, содержимое страниц.
- Перепроверьте весь код на похожие места: где нашлась одна склейка, почти всегда есть ещё.
Пошаговый разбор первых часов после инцидента есть в статье «Взломали сайт на Битрикс: что делать прямо сейчас», он подходит и для других CMS. Если сайт заражён, заблокирован или работает с ошибками, начинают с восстановления сайта после взлома с поиском точки входа, иначе взлом повторится.
Чек-лист: защищён ли ваш сайт от SQL-инъекций
- Все запросы к базе используют параметры или ORM, сырой SQL со склейкой отсутствует или обоснован.
- Поля сортировки, фильтров и поиска проверяются по белому списку.
- Входные данные валидируются на сервере: тип, длина, формат.
- У пользователя базы сайта нет прав на удаление таблиц и запись файлов.
- Ошибки SQL не выводятся посетителям на продакшене.
- CMS, плагины и модули обновлены, заброшенные расширения удалены.
- Включён WAF, логи хранятся и хотя бы раз в месяц просматриваются.
- Пароли пользователей хранятся в bcrypt или Argon2, бэкапы лежат вне сервера.
- Есть назначенный ответственный и план действий на случай утечки, включая уведомление Роскомнадзора.
- Код проверялся на уязвимости после последних крупных доработок.
Итог
SQL-инъекция — старая и хорошо изученная уязвимость, от которой давно есть надёжное средство: параметризованные запросы. Она по-прежнему в OWASP Top 10, потому что живёт в легаси-коде, сторонних модулях и срочных доработках, которые никто не перепроверял. Для бизнеса цена ошибки выросла: утечка клиентской базы — это уведомление регулятора в течение суток, штрафы в миллионы рублей и оборотный штраф при повторе. Проверить код дешевле, чем разбираться с последствиями.
