Урок по треку Безопасность приложений 8 мин

XSS и Content Security Policy

Отражённый, хранимый и DOM XSS, контекстное экранирование, санитайзеры, Content Security Policy и Trusted Types.

XSS (cross-site scripting) это внедрение скрипта в страницу, которую видит другой пользователь. Скрипт выполняется в контексте вашего сайта: читает данные страницы, делает запросы от имени жертвы, подменяет интерфейс. Это инъекция, только интерпретатор здесь браузер.

Три вида XSS

  • Отражённый: вредный ввод приходит в запросе и сразу попадает в ответ. https://site.ru/search?q=<script>…</script>. Жертву заманивают по ссылке.
  • Хранимый: ввод сохраняется (комментарий, имя профиля) и показывается всем, кто откроет страницу. Самый опасный.
  • DOM-based: уязвимость целиком в клиентском JavaScript: данные из location.hash или postMessage попадают в innerHTML, eval, document.write. Сервер может ничего не видеть.

Экранирование по контексту

Главная защита: выводить данные так, чтобы браузер воспринимал их как текст. Правила зависят от контекста:

КонтекстЧто делать
Текст внутри HTMLЭкранировать & < > " ' в сущности
Значение атрибутаЭкранировать и всегда брать атрибут в кавычки
URL в href, srcПроверить схему (только http, https, относительные), затем экранировать
Внутри <script> и обработчиков onclickНе вставлять данные; передавать через data- атрибуты или JSON с экранированием
CSSНе вставлять пользовательские значения

Экранирование HTML не защищает href="javascript:alert(1)": в этой строке нет опасных символов. Поэтому для URL нужна проверка схемы.

Фреймворки

React, Vue, Angular и шаблонизаторы с автоэкранированием (Jinja2, Twig, Thymeleaf) экранируют текст по умолчанию. XSS появляется, когда защиту обходят:

  • dangerouslySetInnerHTML, v-html, [innerHTML], |safe, {!! !!}, th:utext;
  • href={userUrl} без проверки схемы;
  • прямой доступ к DOM: el.innerHTML = …;
  • рендер на сервере с ручной склейкой HTML.

Если нужно показать пользовательский HTML (редактор статей), его пропускают через санитайзер с allowlist тегов и атрибутов, например DOMPurify. Регулярные выражения для этого не годятся.

Content Security Policy

CSP это заголовок, который ограничивает, откуда браузер может загружать и выполнять скрипты. Он не исправляет XSS, а резко снижает последствия. Современная строгая политика на nonce:

Content-Security-Policy: script-src 'nonce-R4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'none'
  • nonce новый случайный на каждый ответ; выполняются только скрипты с этим атрибутом nonce.
  • 'strict-dynamic' разрешает скриптам с nonce подгружать другие скрипты.
  • object-src 'none' отключает плагины, base-uri 'none' не даёт подменить базовый URL.
  • 'unsafe-inline' и 'unsafe-eval' почти полностью обесценивают политику.
  • Политики по списку доменов (script-src cdn.example.com) часто обходятся через JSONP и старые библиотеки на тех же доменах.
  • Внедрять через Content-Security-Policy-Report-Only: собрать нарушения, исправить, затем включить.

Другие полезные директивы: frame-ancestors (защита от кликджекинга, замена X-Frame-Options), form-action, upgrade-insecure-requests.

Trusted Types

Директива require-trusted-types-for 'script' запрещает передавать обычные строки в опасные DOM-приёмники (innerHTML, eval). В них можно передать только объект, созданный политикой, где происходит санитизация. Это закрывает DOM XSS на уровне платформы (поддержка в Chromium-браузерах, в других появляется).

Что помогает дополнительно

  • Cookie сессии с HttpOnly: скрипт не прочитает токен (но всё равно сможет делать запросы от имени жертвы).
  • X-Content-Type-Options: nosniff и правильный Content-Type для пользовательских файлов.
  • Пользовательский контент на отдельном домене (sandbox-домен для загрузок).

Частые ошибки

  • «Мы на React, XSS невозможен».
  • Экранировать при сохранении в базу, а не при выводе (данные потом идут в разные контексты).
  • Санитизировать регулярками.
  • CSP с 'unsafe-inline'.

Что спросят на собесе

  • Виды XSS и чем DOM XSS отличается от остальных.
  • Почему React не защищает от href="javascript:…".
  • Как работает CSP с nonce и почему allowlist доменов слабее.
  • Защищает ли HttpOnly от XSS.
  • Как безопасно показать HTML от пользователя.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.