SQL-инъекция — это когда текст, введённый пользователем, попадает в запрос к базе данных и выполняется как часть запроса. XSS — когда такой текст попадает в страницу и выполняется как программа в браузере другого пользователя. Оба случая закрываются одним принципом: данные и команды не смешиваются. Для базы это параметры запроса, для страницы экранирование при выводе.

Мы разберём, как это устроено, чем отличаются виды атак, что закрывает фреймворк сам и что ограничивает ущерб, если ошибка всё же проскочила. Как проходит проверка у нас и сколько она стоит, описано на странице «Защита от SQL-инъекций и XSS», здесь только смысл и вопросы к подрядчику.

Коротко. SQL-инъекция читает или портит данные через базу, XSS действует через браузер пользователя. Против первой работают параметры запроса, против второй экранирование при выводе. Запретные символы и фильтрация «плохих» слов защищают плохо. Добавьте ограничения ущерба: права базы, настройки cookie и политику CSP.

Как работает SQL-инъекция на примере поиска

Представьте поиск по фамилии. Программист пишет запрос так: «выбрать клиентов, у которых фамилия равна» и подставляет в конец то, что ввёл пользователь, в кавычках. Пока вводят «Иванов», всё работает. Но если после фамилии набрать кавычку и продолжение вроде «или 1 равно 1», кавычка закрывает значение, а продолжение превращается в часть запроса. Условие становится истинным для всех строк, и поиск возвращает всех клиентов.

Это условный пример, но механика настоящая: приложение склеило команду и данные в одну строку, и база не может отличить их друг от друга. Вместо «или 1 равно 1» нападающий может добавить объединение с другой таблицей, чтобы прочитать чужие данные, или команду изменения. Что именно получится, зависит от прав, с которыми приложение работает с базой.

Откуда берутся такие запросы

  • Склейка строк. Самая частая причина: запрос собран из кусков с подстановкой переменных.
  • Сырые запросы внутри ORM. Библиотека работы с базой безопасна, пока ею пользуются как положено. Но в ней есть способ выполнить собственный запрос строкой, и если в него подставить ввод, защита отключена.
  • Динамические части. Имя поля для сортировки, имя таблицы, направление: их нельзя передать параметром, и разработчики подставляют их из запроса напрямую.
  • Данные из «доверенных» мест. Значение из файла, письма или чужой системы, записанное в базу и потом подставленное в другой запрос.

Чем это опасно для бизнеса

Опасность определяется не названием атаки, а тем, что приложение умеет делать с данными. Ниже сравнение двух классов по тому, что теряет заказчик.

Что теряетсяSQL-инъекцияXSS
Чтение чужих данныхВыборка клиентов, заказов, документов, учётных записей напрямую из базыДанные, которые видит пользователь, попавший на подменённую страницу
Изменение данныхПравка и удаление записей, если у приложения есть права на записьДействия от имени пользователя, пока он вошёл в систему
Вход в системуОбход проверки пароля или чтение хешей паролейКража сессии, если cookie не защищены
Кто пострадалВсе данные в базе, доступные пользователю базы приложенияТот, кто открыл страницу: клиент или сотрудник, в том числе администратор
Как заметитьНеобычные запросы к базе, ошибки базы в журналеЧасто никак: нападение идёт в браузере пользователя, и сервер видит обычный запрос

Из таблицы следует главное: SQL-инъекция бьёт по хранилищу, XSS по людям. Опасность XSS растёт, когда страницу открывает администратор, у которого больше прав. В целом ущерб зависит от того, насколько ограничены права, и это можно настроить независимо от кода (смотрите раздел про ограничение ущерба).

Как закрывается SQL-инъекция

Принцип один: запрос и данные передаются базе раздельно. Запрос пишется с пустыми местами, а значения отправляются отдельно, и база воспринимает их только как значения, что бы в них ни лежало. Это называется параметризованным запросом. Ниже, как это выглядит для разных мест кода.

МестоОпасный способБезопасный способ
Значение в условииПодставить введённое в строку запросаПараметр запроса; в ORM — обычный фильтр, а не сырой запрос
Список значенийСклеить значения через запятую в запросПередать список как массив или создать нужное число параметров
Поиск по части словаВставить введённое внутрь шаблона запросаПараметр, в который шаблон добавлен вне запроса; спецсимволы шаблона экранируются отдельно
Имя поля для сортировкиВзять имя из запроса и подставить как естьЗаранее заданный список допустимых полей и выбор по нему
Сырой запрос в ORMСобрать строку с подстановкойТот же параметризованный вариант, который есть в библиотеке
Хранимые процедурыВнутри процедуры собрать запрос из частейПараметры и внутри процедуры, не склейка

Попытка «защититься» фильтрацией кавычек и подозрительных слов ненадёжна: у обхода слишком много вариантов, а настоящие данные вроде фамилии с апострофом начинают ломаться. Параметры решают задачу в корне.

Что добавить вокруг запроса

  • Минимальные права базы. Приложение работает под учётной записью, у которой нет права менять структуру и читать чужие схемы. Если инъекция всё же случится, она увидит меньше.
  • Отдельная запись для чтения. Отчёты и поиск идут под учётной записью без права записи.
  • Скрытые ошибки. Текст ошибки базы не показывается пользователю, он уходит в журнал.
  • Тесты. Проверка на ввод с кавычкой для каждого места, куда пользователь что-то вводит.

Как работает XSS и какие у него виды

XSS (межсайтовое выполнение сценариев) возникает, когда приложение выводит введённый пользователем текст на страницу как разметку. Если в текст вставить небольшую программу для браузера, она выполнится от имени вашего сайта и получит доступ ко всему, что доступно странице: данные на экране, действия пользователя, иногда его сессия.

ВидКак попадаетКого задеваетУсловный пример
СохранённыйВредный текст записан в базу и показывается при каждом открытии страницыВсе, кто открыл страницу, включая сотрудниковКомментарий к заказу, в который вставлена разметка, и менеджер открывает заказ
ОтражённыйТекст из адреса или формы сразу выводится в ответеТот, кто перешёл по специально составленной ссылкеСтраница поиска, повторяющая запрос в заголовке без обработки
На стороне браузераСкрипт страницы сам берёт данные из адреса и вставляет их в страницуТот, кто открыл ссылкуСкрипт показывает часть адреса на странице как разметку

Отличие от SQL-инъекции в том, что цель нападающего не сервер, а человек. Поэтому защита делится на две части: не дать тексту стать кодом и ограничить, что код может сделать, если стал.

Как закрывается XSS: правило зависит от места вывода

Универсального «обезвреживания» нет: правильное преобразование зависит от того, куда именно вставляется текст. Одно и то же значение безопасно в тексте страницы, но опасно в адресе или в скрипте.

Куда выводимЧто делатьЧастая ошибка
Текст внутри страницыЭкранировать символы разметки, чтобы они показывались, а не выполнялись; современные шаблонизаторы делают это самиОтключить экранирование «для красоты» или вывести как готовую разметку
Значение атрибутаЭкранирование с учётом кавычек, значение всегда в кавычкахВставить значение без кавычек
Адрес в ссылкеРазрешать только известные схемы адресов (http, https, mailto)Принять любой адрес, включая исполняемые схемы
Внутри скриптаНе подставлять данные в текст скрипта; передавать их как данные через отдельный блок или запросСобрать скрипт строкой
Разметка от пользователяЕсли нужно разрешить форматирование, пропускать через проверенную библиотеку очистки с белым списком теговПисать свой фильтр тегов
Ответ API для браузераПравильный тип содержимого и никакого вывода как страницыОтвет с данными отдаётся с типом страницы

Современные интерфейсные библиотеки (React, Vue) экранируют вывод по умолчанию. Опасность появляется там, где разработчик сознательно обходит защиту, чтобы вставить готовую разметку, или подключает чужой скрипт. Эти места стоит перечислить и проверить отдельно.

Что ограничивает ущерб, если ошибка проскочила

Идеальной защиты не бывает, и опытные команды строят вторую линию, которая работает независимо от кода вывода.

  • Политика CSP. Заголовок ответа, в котором сайт заранее перечисляет, откуда браузеру можно загружать скрипты. Подставленный скрипт тогда не выполняется. Вводить её лучше сначала в режиме отчётов, чтобы не сломать страницы.
  • Cookie с флагами. Флаг, запрещающий скриптам читать cookie сессии, и ограничение отправки на чужие сайты. Тогда кража сессии через XSS заметно сложнее.
  • Подтверждение опасных действий. Смена пароля, почты, платёжных реквизитов и выдача прав требуют повторного ввода пароля или второго фактора. Подменённая страница не может ввести пароль вместо пользователя.
  • Раздельные адреса. Пользовательские файлы и загруженные документы отдаются с другого адреса, чем кабинет.
  • Права базы. Описаны выше: минимальные права и учётная запись для чтения у отчётов.

Ни одна из этих мер не заменяет правильный код, но вместе они превращают «полный доступ» в «ограниченный ущерб».

Как проверить защиту у подрядчика и у себя

Проверки делайте только на тестовой копии своей системы. Нападение на чужие системы и боевую без согласования недопустимо.

Вопросы разработчику

  1. Покажите, как собирается запрос к базе в трёх местах: поиск, фильтры и отчёт. Есть ли склейка строк?
  2. Где в проекте используются сырые запросы и вывод готовой разметки? Есть ли их список?
  3. Под какой учётной записью приложение работает с базой, какие у неё права?
  4. Включена ли CSP, какие флаги у cookie сессии?
  5. Какие тесты проверяют ввод с кавычкой и разметкой и запускаются ли они в сборке?

Что можно проверить на тестовой копии

Введите в поля поиска и формы фамилию с апострофом и пару знаков разметки. Здоровое приложение примет их как обычный текст и покажет как есть. Ответ «внутренняя ошибка» на апостроф — повод насторожиться: возможно, запрос собирается склейкой. Текст разметки, который выводится в оформленном виде, а не как набранные знаки, указывает на отключённое экранирование.

Эти пробы выявляют очевидное. Автоматические сканеры находят типовые случаи, но пропускают нестандартные пути ввода и данные, которые попали в базу в другом месте. Поэтому серьёзную проверку дополняет чтение кода, как описано в статье «OWASP Top 10 простыми словами» и на странице Secure code review.

Что делаем мы и с чего начать

Если вопрос ограничен этими двумя классами ошибок, берём проверку одного приложения: 200 000 ₽ и 2–3 недели, нескольких приложений вместе со стыками — 350 000 ₽ и 3–4 недели. На выходе места в коде, воспроизведение, серьёзность, способ исправления и тесты, которые не дают ошибке вернуться. Если нужна шире проверка безопасности, смотрите аудит безопасности веб-приложения. Цены по ступеням собраны на странице «Тарифы», сумма обследования вычитается из первого этапа, если вы продолжаете с нами.

Исправление найденного оценивается по отчёту, от 150 000 ₽ за этап. Мы читаем код и конфигурацию и воспроизводим находки на вашем стенде, а не атакуем боевую систему. Не проводим пентест и аттестацию, лицензии ФСТЭК у нас нет, WAF и SIEM как услугу не предоставляем, не дежурим круглосуточно. Остановиться можно после любой ступени.

Частые вопросы

Что такое SQL-инъекция простыми словами?

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

Что такое XSS и чем он отличается от SQL-инъекции?

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

Достаточно ли фильтровать кавычки и опасные слова?

Нет. Фильтрация плохо работает: у обхода много вариантов, а настоящие данные (фамилия с апострофом) начинают ломаться. Против SQL-инъекций работают параметры запроса, против XSS экранирование при выводе с учётом места вывода.

Защищает ли нас фреймворк или ORM?

Во многом да: типовые запросы и вывод в шаблонах они делают безопасно. Но защита отключается там, где пишут сырой запрос, выводят готовую разметку или подставляют имя поля из запроса. Эти места нужно перечислить и проверить.

Что такое CSP и нужна ли она?

CSP — заголовок, в котором сайт перечисляет, откуда браузеру разрешено загружать скрипты. Она ограничивает ущерб от XSS и не заменяет правильный вывод. Вводить её лучше сначала в режиме отчётов, чтобы не сломать страницы.

Как проверить, что в приложении нет этих ошибок?

Сочетанием способов: автоматическая проверка находит типовые случаи, чтение кода находит нестандартные пути ввода, ручной прогон на стенде подтверждает находки. Проверки делают на тестовой копии своей системы, а не на чужой и не на боевой без согласования.

Сколько стоит проверка на эти уязвимости?

Проверка одного приложения — 200 000 ₽ и 2–3 недели, нескольких приложений со стыками — 350 000 ₽ и 3–4 недели. Исправление найденного оценивается по отчёту, от 150 000 ₽ за этап. Обследование вычитается из первого этапа, если вы продолжаете с нами.

Нужен ли для защиты WAF?

WAF может отсекать часть типовых попыток, но он не заменяет исправление кода: обход фильтров по шаблонам реален. Основа защиты находится в приложении. WAF как услугу мы не предоставляем, мы исправляем причину в коде.