Защита от SQL-инъекций и XSS-атак: ревью кода, исправление и тесты, которые не дают ошибке вернуться

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

Проверка — от 200 000 ₽, 2–3 неделиКаждая находка с воспроизведением, исправлением и тестомКод остаётся в вашем контуре и вашем репозитории

С чем приходят

Когда одной «заплатки» мало

Сканер или клиент нашёл инъекцию

В отчёте одна страница с пометкой «высокая серьёзность». Исправить её просто, но неизвестно, сколько таких мест рядом.

Запросы собираются строками

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

Приняли код от подрядчика

Известно, что работает, неизвестно, как устроен вывод в шаблонах и как собираются запросы. Нужен срез по этим двум классам ошибок до выхода к клиентам.

Служба безопасности клиента требует подтверждения

Прислали опросник: как защищены запросы к базе и вывод на страницу. Ответ должен опираться на проверенный код и тесты.

SQL

Где в приложении рождаются инъекции: места, причины и исправление

Инъекции редко сидят в простом «найти по номеру». Чаще они прячутся там, где параметр нельзя подставить обычным способом. Общая проверка по списку OWASP — на странице аудита безопасности веб-приложения.

Место в коде
Почему возникает
Чем закрываем
Как проверяем
Место в кодеПоиск и фильтрыусловия из формы
Почему возникаетУсловие собирают из частей строки, потому что набор фильтров переменный
Чем закрываемСписок условий строится кодом, значения передаются отдельными параметрами
Как проверяемЧитаем сборку запроса и подаём на стенде строки с кавычками во все поля
Место в кодеСортировкаимя столбца из адреса
Почему возникаетИмя столбца и направление нельзя передать параметром, поэтому их вставляют как есть
Чем закрываемБелый список допустимых полей: из адреса берём ключ, а имя столбца выбирает код
Как проверяемПодставляем неизвестное имя поля и смотрим, что вернёт сервер
Место в кодеСырые запросы в ORMотчёты, сложные выборки
Почему возникаетРазработчик уходит от ORM ради скорости или сложного соединения и форматирует строку
Чем закрываемТот же запрос с параметрами; для отчётов отдельная учётная запись только на чтение
Как проверяемПоиск по коду всех мест с сырым SQL и разбор каждого вручную
Место в кодеФильтры ORM из тела запросасловарь целиком
Почему возникаетСловарь условий из запроса передаётся в фильтр: пользователь выбирает поля и операторы сам
Чем закрываемДопустимые поля и операторы перечислены явно; остальное отбрасывается
Как проверяемЗапрос с полями связанных таблиц, которых в интерфейсе нет
Место в кодеДанные из самой базывторичная инъекция
Почему возникаетЗначение сохранили безопасно, а потом достали и подставили в другой запрос как доверенное
Чем закрываемДоверия «внутренним» данным нет: параметры везде, откуда бы значение ни пришло
Как проверяемСохраняем значение с кавычками и проходим все места, где оно читается
Принцип

Почему параметры, а не фильтрация кавычек

Самая частая «защита» в старом коде — вырезать или экранировать опасные символы на входе.

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

Почему ручное экранирование не метод

  • Правила зависят от контекста. Строка, числовое поле, имя столбца и значение внутри шаблона экранируются по-разному, и одна функция на все случаи не подходит.
  • Фильтр на входе защищает одно место. Данные приходят ещё из файлов, обмена с другими системами и из самой базы. Защита должна стоять там, где строится запрос, а не у двери.
  • Чёрные списки обходятся. Запретить слова и символы нельзя: законное имя клиента или наименование товара может содержать любые из них.

Что ещё входит в защиту базы

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

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

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

Куда выводим
Что опасно
Как закрываем
Типичная ошибка
Куда выводимТекст в теле страницыимя, комментарий, название
Что опасноУгловые скобки и амперсанд превращаются в безопасные символы
Как закрываемШаблонизатор кодирует вывод сам, пока кто-то не включает режим «доверенного» вывода
Типичная ошибкаМетка «безопасно» на поле, которое заполняет пользователь
Куда выводимСсылкаадрес из профиля или из параметра
Что опасноПроверяем схему адреса: разрешены только http и https и внутренние пути
Как закрываемАдрес вида javascript: проходит как «ссылка»
Типичная ошибкаПеренаправление на адрес из параметра без проверки
Куда выводимВнутри скриптаданные для клиентской части
Что опасноДанные не вставляются в код строкой; передаются как JSON в отдельном блоке или в data-атрибуте
Как закрываемПеременная скрипта заполняется прямо из шаблона
Типичная ошибкаЗакрывающий тег скрипта внутри данных
Куда выводимHTML от пользователяписьма, заметки, описание товара
Что опасноСанитайзер по белому списку тегов и атрибутов, на сервере и при выводе
Как закрываемЧистка регулярными выражениями вместо библиотеки
Типичная ошибкаРазметка проверена при сохранении, но шаблон потом поменялся
Куда выводимФайлыSVG, HTML, загруженные документы
Что опасноОтдаются как вложение, с точным типом содержимого и запретом угадывать тип; пользовательские файлы живут на отдельном домене
Как закрываемФайл открывается на домене самого приложения и исполняет скрипт
Типичная ошибкаТип определяется по расширению, которое прислал пользователь
Фреймворки

Что фреймворк закрывает сам и где защита отключается

Современные шаблонизаторы кодируют вывод по умолчанию. Уязвимости появляются в тех местах, где защиту выключили ради удобства.

Мы ищем в вашем коде обходы защиты, они хорошо видны поиском:

  • Явные «сырые» выводы. Прямая вставка разметки в React и Vue, фильтры и метки «безопасно» в Django и Jinja, обход проверки в Angular. Каждое место либо убирается, либо получает санитайзер.
  • Работа с деревом страницы вручную. Присваивание готовой разметки через innerHTML, вставка из адресной строки и из сообщений между окнами. Такие участки переписываются на безопасные методы, которые вставляют текст, а не разметку.

Чужой код на вашей странице

Метрика, чат-виджет и скрипт рекламной сети исполняются с теми же правами, что и ваш код. Если их владелец взломан, вы получаете XSS без единой уязвимости у себя. Мы составляем перечень внешних скриптов, убираем лишние, для статических файлов с чужих серверов включаем проверку целостности и ограничиваем разрешённые источники политикой CSP.

Второй рубеж

Если XSS всё же проскочит: что ограничивает ущерб

Политика CSP

Заголовок Content-Security-Policy говорит браузеру, откуда можно исполнять скрипты. Строгая политика с одноразовыми метками (nonce) блокирует встроенные скрипты, которые внёс злоумышленник. Включаем сначала в режиме отчётов, чтобы увидеть, что сломается, и только потом в блокирующем.

Cookie сессии

Флаг HttpOnly не даёт скрипту прочитать сессию, Secure отправляет её только по защищённому соединению, SameSite ограничивает отправку с чужих страниц. Токены в localStorage доступны любому скрипту страницы, поэтому для сессии выбираем cookie.

Опасные действия требуют подтверждения

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

Заголовки ответа

X-Content-Type-Options запрещает браузеру угадывать тип файла, а запрет встраивания страницы в чужие окна закрывает подмену интерфейса.

Метод

Как проверяем: автоматика, чтение кода и ручной прогон

Ни один способ в одиночку не находит всё, поэтому сочетаем их.

Способ
Что делаем
Что находит
Что пропускает
СпособПоиск по кодусборка запросов и вывода
Что делаемВыделяем все места с сырым SQL, форматированием строк, прямой вставкой разметки
Что находитСписок мест, где защита обойдена или отсутствует, и порядок их разбора
Что пропускаетОценку риска: найденное место может быть недостижимым, что видно только при чтении
СпособЧтение рискованных участковвручную
Что делаемИдём по пути данных от запроса до базы и от базы до страницы
Что находитВторичные инъекции, обходы в обработчиках, ошибки в самописных санитайзерах
Что пропускаетВсё, что в объёме кода не дошло до чтения; объём фиксируется в договоре
СпособПрогон на стендетестовые значения в каждое поле
Что делаемСтроки с кавычками, тегами и спецсимволами подаём во все поля, заголовки, JSON и имена файлов
Что находитПодтверждает находку воспроизведением и отсекает ложные срабатывания
Что пропускаетУчастки, до которых нужны сложные данные: цепочки согласований, интеграции
СпособТесты в сборкепосле исправления
Что делаемДля каждого исправленного места добавляем тест, который падает при возврате ошибки
Что находитРегрессию на следующем релизе
Что пропускаетНовый код в новых местах: его закрывает правило разработки и ревью
Что делаем

Из чего состоит работа и сколько она стоит

Цены совпадают со шкалой тарифов и с аудитом кода: цена зависит от объёма кода, а не от числа слов в заголовке. Работы дешевле 150 000 ₽ мы не берём.

Проверка одного приложения

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

200 000 ₽ · 2–3 недели

Проверка нескольких приложений

Сайт, кабинет, API и фоновые сервисы вместе, плюс стыки между ними.

350 000 ₽ · 3–4 недели

Исправление найденного

Правки в коде вместе с вашими разработчиками или вместо них, по каждой находке тест. Этап с приёмкой, оценка по отчёту.

после проверки · от 150 000 ₽

Защитные правила в проекте

Правила статического анализа в сборке, тесты на инъекции, заголовки и CSP в режиме отчётов, памятка для разработчиков о том, как писать запросы и вывод.

после обследования · от 150 000 ₽

Сопровождение

Проверка новых релизов на эти классы ошибок, разбор обращений, обновление зависимостей. Сверх пакета — 5 000 ₽ в час.

20 000 / 60 000 / 120 000 ₽ в месяц
Процесс

Как идёт работа

До начала — Рамки

NDA, согласование объёма и стенда с вашей службой безопасности. Определяем, какой код читаем и чего не трогаем. Боевую систему без вашего письменного согласия не затрагиваем.

Дни 1–3 — Карта точек входа

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

Недели 1–2 — Проверка

Поиск по коду, чтение, прогон на стенде. О серьёзных находках сообщаем сразу, не дожидаясь отчёта.

Конец срока — Отчёт и разбор

Находки с воспроизведением и способом исправления, сводка для руководителя, разбор с вашими разработчиками.

После — Исправление и тесты

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

Контроль

Что остаётся у вас и чего мы не делаем

Остаётся у вас

  • Отчёт и воспроизведение каждой находки: наши черновики после сдачи не хранятся.
  • Тесты, правила анализа и заголовки в вашем репозитории с первого коммита.
  • Исходный код остаётся в вашем контуре; на свои машины мы его не копируем без письменного согласия.
  • Право остановиться после проверки: исправление у нас заказывать не обязательно.

Мы этого не делаем

  • Не атакуем боевые системы и не проводим пентест, не проверяем защиту сети.
  • Не настраиваем WAF и не подбираем его правила: он прикрывает ошибку, но не исправляет её.
  • Не защищаем от DDoS и не ведём круглосуточный мониторинг; дежурства 24/7 у нас нет.
  • Не проводим аттестацию и не выдаём заключений о соответствии требованиям регуляторов, лицензии ФСТЭК у нас нет.
  • Не обещаем, что в приложении не осталось уязвимостей: мы закрываем два класса ошибок и оставляем проверки, которые помогают не вернуть их.
  • Не берём работы дешевле 150 000 ₽.
Граница

Защита кода, WAF и аудит: что выбрать и в каком порядке

Межсетевой экран для веб-приложений (WAF) фильтрует подозрительные запросы снаружи. Он полезен как временная мера, пока исправление в очереди, но обход фильтра находят, а ошибка в коде остаётся. Мы чиним причину. Настройка и эксплуатация WAF как услуга в наш перечень не входит.

Если приложение ещё не проверялось вообще, начинают с аудита безопасности: он покрывает все десять классов, а инъекции и XSS в нём лишь два пункта. Если известно, что проблема именно здесь, подойдёт эта проверка.Защита входа и прав описана на странице SSO и MFA, защита самого API от перебора и чужих запросов — на странице защиты API.

Инструменты

Чем проверяем и что оставляем в проекте

Анализ и проверка
  • Поиск по коду и правила статического анализа под ваш язык
  • Прокси для просмотра запросов и ручной прогон на стенде
  • Сканеры уязвимостей как вспомогательное средство, не как заключение
  • Санитайзеры HTML, проверенные библиотеки
Что остаётся в сборке
  • Тесты в вашем наборе (pytest, Jest или ваш аналог)
  • Правила анализа в GitLab CI или в вашей системе сборки
  • Заголовки безопасности в конфигурации Nginx или приложения
  • Отчёты CSP, собираемые в журнал
Материалы

Материалы по теме

Статья

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

Как работают обе атаки, как они закрываются и что проверить у себя за час на тестовой копии.

  • Как работают
  • Как закрываются
  • Проверка за час
Читать →
Статья

OWASP Top 10 простыми словами для заказчика

Десять классов ошибок без жаргона. Инъекции и XSS в нём стоят рядом с ошибками прав и настроек.

  • Десять классов
  • Вопросы подрядчику
  • Как читать отчёт
Читать →
Статья

Как составить техническое задание на разработку

Требования к безопасности записываются в задание и проверяются при приёмке.

  • Что писать
  • Критерии приёмки
  • Типичные пропуски
Читать →
Заявка

Расскажите, что нужно проверить

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

Спасибо, заявка отправлена. Ответим в течение рабочего дня.

Не удалось отправить. Напишите в телеграм или позвоните.

Вопросы

Частые вопросы: защита от SQL-инъекций и XSS

Сколько стоит проверка на SQL-инъекции и XSS?

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

Достаточно ли ORM, чтобы не бояться инъекций?

Нет. ORM защищает обычные запросы, но сырые вставки, форматирование строк, динамические имена столбцов и передача условий целиком возвращают проблему. Мы ищем именно эти места.

Если у нас современный фреймворк, XSS быть не может?

Может. Шаблонизаторы кодируют вывод по умолчанию, но в каждом есть способ вставить разметку как есть, и его включают ради форматирования текста. Мы проверяем такие места, самописные санитайзеры, работу с деревом страницы вручную и сторонние скрипты.

Нужно ли ставить WAF?

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

Вы будете атаковать наш рабочий сайт?

Нет. Мы читаем код и конфигурацию, а тестовые значения подаём на отдельном стенде с обезличенными данными. Боевую систему без вашего письменного согласия не затрагиваем, и в любом случае это не пентест.

Что мы получим по итогам?

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

Как защита встраивается в разработку, чтобы не вернулись такие же ошибки?

Тремя вещами: правила анализа и тесты в сборке, ревью с чек-листом по запросам и выводу, памятка для команды с примерами из вашего кода.

Работаете ли вы с закрытым контуром?

Да. Можно читать код у вас, без выноса наружу, и работать на стенде внутри контура.

Вы работаете только в Ростове-на-Дону?

Мы в Ростове-на-Дону, работаем с заказчиками по всей России: удалённо и в вашем контуре. Доступ к системам и обследование согласуем на старте с вашей службой безопасности.