Броня для фронтенда: как настроить Content Security Policy (CSP) и наглухо закрыть сайт от XSS

Сайт может быть написан на чистом коде, регулярно обновляться и стоять за Cloudflare, но одна-единственная дыра в стороннем плагине, форме комментариев или забытом виджете чата открывает прямую дорогу для XSS-инъекций (Cross-Site Scripting). Злоумышленнику достаточно подсунуть вредоносный <script>, чтобы перехватить клиентские сессии, украсть авторизационные куки, подменить платежные реквизиты или превратить браузеры посетителей в ферму для майнинга.

Большинство разработчиков пытаются спасаться экранированием пользовательского ввода на бэкенде. Это базовая гигиена, но полагаться только на нее наивно. Вторым и самым жестким эшелоном обороны выступает CSP (Content Security Policy) — HTTP-заголовок, который диктует браузеру четкие правила: откуда можно грузить скрипты, стили, шрифты и картинки, а любые неавторизованные попытки исполнения чужого кода рубит на корню.

Как работает механизм CSP на практике

CSP превращает браузер клиента в неподкупного валидатора. Сервер вместе с HTML-страницей отдает в заголовках белый список доверенных источников. Если на странице вдруг оказывается JS-код со стороннего вредоносного домена или инлайн-скрипт, внедренный через уязвимый инпут, браузер просто отказывается его выполнять и выбрасывает ошибку в консоль.

Ключевые директивы, из которых собирается рабочая политика:

  • default-src — базовый уровень для всех типов ресурсов, если для них не заданы индивидуальные правила. Обычно выставляется в 'self' (только текущий домен).
  • script-src — главный рубеж против XSS. Определяет доверенные хосты, с которых разрешено подтягивать и выполнять JavaScript.
  • style-src — регулирует загрузку CSS-таблиц и предотвращает атаки через подмену стилей (CSS Injection).
  • img-src и font-src — изолируют источники графики и кастомных веб-шрифтов (например, Google Fonts или локальные WOFF2).
  • connect-src — жестко ограничивает эндпоинты, на которые фронтенд может отправлять данные через fetch, XHR или WebSocket.
  • frame-ancestors — запрещает встраивать ваш сайт в чужие <iframe>, полностью нейтрализуя кликджекинг (Clickjacking).

Примеры настройки заголовков: от боевого конфига до веб-сервера

Настройка Content Security Policy всегда начинается с проектирования политики под конкретный стек проекта. Если на сайте подключены внешние счетчики аналитики (Яндекс Метрика, Google Analytics) и CDN-шрифты, базовый заголовок будет выглядеть так:

Content-Security-Policy: default-src 'self'; script-src 'self' [https://mc.yandex.ru](https://mc.yandex.ru) [https://www.googletagmanager.com](https://www.googletagmanager.com); style-src 'self' 'unsafe-inline' [https://fonts.googleapis.com](https://fonts.googleapis.com); font-src 'self' [https://fonts.gstatic.com](https://fonts.gstatic.com); img-src 'self' data: [https://mc.yandex.ru](https://mc.yandex.ru); connect-src 'self' [https://mc.yandex.ru](https://mc.yandex.ru); frame-ancestors 'none';

Внедрить заголовок на уровне сервера можно несколькими путями:

  • Конфигурация Nginx. Самый производительный вариант. Добавьте директиву в блок server или location: add_header Content-Security-Policy "default-src 'self'; script-src 'self' [https://trusted.cdn.com](https://trusted.cdn.com);" always;
  • Конфигурация Apache (.htaccess). Если проект работает на Apache, используйте модуль заголовков: Header always set Content-Security-Policy "default-src 'self'; script-src 'self';"
  • Через мета-тег в HTML. Запасной вариант, если нет прямого доступа к конфигурации сервера: <meta http-equiv="Content-Security-Policy" content="...">. Учтите, что через мета-тег нельзя передать некоторые директивы вроде frame-ancestors и report-uri.

Пошаговый пайплайн безопасного внедрения без поломки сайта

Главный страх при внедрении жесткой политики — случайно сломать верстку, интерфейс личного кабинета или отрезать маркетинговую аналитику. Чтобы выкатка на боевой сервер прошла гладко, используйте проверенный алгоритм:

  1. Запуск в режиме мониторинга (Report-Only). Вместо основного заголовка отдавайте Content-Security-Policy-Report-Only с параметром report-uri /csp-report-endpoint/. В этом режиме браузер ничего не блокирует, но аккуратно отправляет JSON-отчеты о каждом нарушении на ваш сервер или в сервис вроде Sentry.
  2. Анализ логов и сбор белого списка. Покрутите сайт в режиме Report-Only неделю под реальным трафиком. Вы сразу увидите все легитимные сторонние скрипты, трекеры и динамические виджеты, о которых забыли фронтендеры, и внесете их в конфиг.
  3. Отказ от ‘unsafe-inline’ через Nonce. Инлайн-скрипты внутри HTML — главная брешь. Чтобы не открывать шлюзы директивой 'unsafe-inline', внедряйте механизм одноразовых токенов (Nonce): сервер генерирует случайную строку для каждого запроса, отдает ее в CSP (script-src 'nonce-R4nd0m123') и проверяет наличие этого атрибута в теге <script nonce="R4nd0m123">.
  4. Финальное переключение в боевой режим. Когда в логах мониторинга перестанут сыпаться ложные срабатывания, замените заголовок на строгий Content-Security-Policy.

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

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (Пока оценок нет)
Загрузка...
logo