SQL-инъекция — это когда текст, введённый пользователем, попадает в запрос к базе данных и выполняется как часть запроса. XSS — когда такой текст попадает в страницу и выполняется как программа в браузере другого пользователя. Оба случая закрываются одним принципом: данные и команды не смешиваются. Для базы это параметры запроса, для страницы экранирование при выводе.
Мы разберём, как это устроено, чем отличаются виды атак, что закрывает фреймворк сам и что ограничивает ущерб, если ошибка всё же проскочила. Как проходит проверка у нас и сколько она стоит, описано на странице «Защита от SQL-инъекций и XSS», здесь только смысл и вопросы к подрядчику.
Как работает 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 заметно сложнее.
- Подтверждение опасных действий. Смена пароля, почты, платёжных реквизитов и выдача прав требуют повторного ввода пароля или второго фактора. Подменённая страница не может ввести пароль вместо пользователя.
- Раздельные адреса. Пользовательские файлы и загруженные документы отдаются с другого адреса, чем кабинет.
- Права базы. Описаны выше: минимальные права и учётная запись для чтения у отчётов.
Ни одна из этих мер не заменяет правильный код, но вместе они превращают «полный доступ» в «ограниченный ущерб».
Как проверить защиту у подрядчика и у себя
Проверки делайте только на тестовой копии своей системы. Нападение на чужие системы и боевую без согласования недопустимо.
Вопросы разработчику
- Покажите, как собирается запрос к базе в трёх местах: поиск, фильтры и отчёт. Есть ли склейка строк?
- Где в проекте используются сырые запросы и вывод готовой разметки? Есть ли их список?
- Под какой учётной записью приложение работает с базой, какие у неё права?
- Включена ли CSP, какие флаги у cookie сессии?
- Какие тесты проверяют ввод с кавычкой и разметкой и запускаются ли они в сборке?
Что можно проверить на тестовой копии
Введите в поля поиска и формы фамилию с апострофом и пару знаков разметки. Здоровое приложение примет их как обычный текст и покажет как есть. Ответ «внутренняя ошибка» на апостроф — повод насторожиться: возможно, запрос собирается склейкой. Текст разметки, который выводится в оформленном виде, а не как набранные знаки, указывает на отключённое экранирование.
Эти пробы выявляют очевидное. Автоматические сканеры находят типовые случаи, но пропускают нестандартные пути ввода и данные, которые попали в базу в другом месте. Поэтому серьёзную проверку дополняет чтение кода, как описано в статье «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 как услугу мы не предоставляем, мы исправляем причину в коде.