Проблема проявляется после XSS или downgrade-атаки: приложение отдаёт страницу по HTTPS, но разрешает любой inline script или продолжает открываться по HTTP. Цена ошибки — выполнение чужого кода в контексте origin, утечка токена и ложное ощущение, что один security header закрыл весь риск.
Причина — копировать длинную строку заголовка без модели ресурсов. CSP ограничивает, откуда браузер может загружать или выполнять ресурсы; HSTS заставляет браузер обращаться к домену по HTTPS после получения политики. Ни один из заголовков не исправляет серверный XSS, плохой сертификат или секрет, уже попавший в JavaScript.
CSP начинается с карты ресурсов
Сначала перечислите, что странице действительно нужно: собственные scripts, стили, изображения, API и frame. Затем для каждого типа выберите минимальную директиву. default-src задаёт fallback, но не объясняет исключения; script-src управляет JavaScript, object-src none закрывает старый plugin-механизм, а base-uri self не даёт странице незаметно изменить базовый URL.
Nonce применяют к конкретному inline script, когда убрать inline-код сразу нельзя. Значение должно быть непредсказуемым и новым для ответа; статическая строка превращается в разрешение для любого, кто её узнал. Шаблон должен вставить nonce и в CSP, и в атрибут script, а логирование полного значения создаёт лишний риск.
| Директива | Что ограничивает | Частая ошибка | Проверка |
|---|---|---|---|
| default-src | fallback для типов ресурсов | считать её полной политикой | проверить исключения по типам |
| script-src | источники JavaScript и nonce | добавить unsafe-inline навсегда | найти все inline и third-party scripts |
| object-src | plugin/object загрузку | оставить широкое значение | поставить none, если object не нужен |
| base-uri | изменение базового URL | забыть директиву | ограничить self или отключить |
| report-only | наблюдение нарушений | принять отчёт за блокировку | после анализа перейти к enforce |
HSTS имеет момент включения
HSTS действует после того, как браузер получил заголовок через доверенный HTTPS-ответ. Он не защищает самый первый HTTP-переход, если домен ещё не известен браузеру; для этого существует отдельная политика preload с собственными требованиями и риском. includeSubDomains распространяет правило на поддомены, поэтому включать его можно только после проверки всех нужных имён.
Большой max-age нельзя трактовать как кнопку «попробовать». Если поддомен ещё не умеет HTTPS, браузер перестанет подключаться к нему по HTTP на весь период. Перед расширением политики проверьте redirect, сертификаты, mixed content и административные endpoint. Безопасность заголовка включает и возможность восстановить ошибочную конфигурацию.
Runnable-пример: собрать минимальные headers
Функция принимает nonce длиной не менее 16 символов и max-age HSTS. Она возвращает набор заголовков или явную ошибку входа. Это учебный генератор: он не устанавливает response headers и не проверяет ваш шаблонизатор. Ожидаемый результат показывает, что CSP содержит nonce, а HSTS — числовой срок и includeSubDomains.
import { buildSecurityHeaders } from './upgrade-2027-12.mjs';
const result = buildSecurityHeaders({
nonce: '7c2f1b8e9a4d6f0c',
hstsMaxAge: 31536000,
});
console.log(result.ok);
console.log(result.headers['Content-Security-Policy']);
console.log(result.headers['Strict-Transport-Security']);
// true
// default-src 'self'; script-src 'self' 'nonce-7c2f1b8e9a4d6f0c'; object-src 'none'; base-uri 'self'
// max-age=31536000; includeSubDomainsПорядок внедрения
- Соберите список ресурсов страницы и найдите inline scripts, eval, object, iframe, внешние CDN и API. Не начинайте с копирования чужой политики.
- Включите CSP в Report-Only и соберите нарушения по URL, директиве и типу ресурса. Отчёт не блокирует выполнение, поэтому не называйте его исправлением.
- Уберите лишние источники, замените inline-код на файл или nonce и добавьте тест на отсутствие unsafe-inline и unsafe-eval без обоснования.
- Переведите CSP в enforce на одной проверяемой странице и сравните ошибки загрузки с разрешённым списком.
- Включите HSTS только после проверки HTTPS для основного домена и поддоменов. Начните с контролируемого max-age, затем расширяйте.
- Проверьте rollback конфигурации: изменение header должно быть версионируемым, а не ручной правкой в одном proxy.
Почему nonce не лечит XSS
Nonce разрешает конкретные скрипты, но не санитизирует пользовательский HTML и не исправляет небезопасный sink. Если приложение вставляет строку в innerHTML, разрешённый bootstrap может помочь атакующему выполнить уже загруженный код. CSP снижает последствия и ловит часть нарушений, но контекстное экранирование и безопасные API остаются обязательными.
Диагностические отчёты CSP тоже требуют осторожности: URL может содержать чувствительные параметры, а third-party ресурс может присылать много шума. В отчёте храните только нужные поля, ограничивайте доступ и отделяйте нарушение политики от подтверждённой уязвимости. Заголовок — контроль браузера, не verdict о безопасности приложения.
Ограничения и следующий шаг
Генератор не проверяет браузерную поддержку, CDN, service worker, iframe-политику, сертификаты и preload. CSP Level 3 — рабочая редакция W3C, поэтому конкретную совместимость и статус директивы нужно сверять с целевыми браузерами. RFC 6797 не защищает первый небезопасный переход и не заменяет TLS.
Следующий шаг — взять одну страницу, собрать Report-Only нарушения, закрыть источники по одному и добавить автоматический тест на заголовки. После enforce отдельно проверьте HSTS на каждом поддомене и храните процедуру возврата конфигурации рядом с кодом, чтобы ошибка не требовала ручной импровизации.
Проверяемые источники
- W3C Content Security Policy Level 3 — W3C, CSP Level 3 Working Draft, опубликованная редакция спецификации. Применение: Описывает Content-Security-Policy, директивы источников, nonce и режим Report-Only/Enforce. Граница: Working Draft может изменяться; конкретную поддержку браузеров и собственные inline-скрипты нужно проверить отдельно.
- RFC 6797 — HTTP Strict Transport Security — IETF, ноябрь 2012 года, RFC 6797, Standards Track. Применение: Определяет Strict-Transport-Security и поведение браузера после получения политики по HTTPS. Граница: Не исправляет mixed content, сертификат, redirect до первого безопасного ответа и настройки API-клиента.