CSP без ложных обещаний: nonce, отчёты и запрет чужого встраивания
Как браузер читает Content Security Policy, почему отчёт не равен блокировке и какие директивы нельзя заменить общим default-src.

Политика приходит от сайта к браузеру
Content Security Policy задаёт ограничения для документа: откуда разрешено получать ресурсы, какие скрипты допустимы и какие внешние документы могут его встраивать. Основной способ передачи — HTTP-заголовок Content-Security-Policy. Значение состоит из директив, разделённых точками с запятой; внутри каждой перечисляются допустимые источники либо особые выражения.
У разных категорий ресурсов собственные директивы. script-src относится к JavaScript, img-src — к изображениям, style-src — к стилям. default-src служит резервным правилом для определённых директив загрузки, а не универсальным запретом всех неупомянутых возможностей браузера. Например, явная настройка img-src меняет правило для изображений, не отменяя ограничений остальных категорий. Схема MDN разделяет название директивы и выражения источников.

Nonce и hash подтверждают конкретный код
Строгая политика может доверять скриптам на основании nonce либо хэша, а не только списка разрешённых доменов. Nonce — непредсказуемое одноразовое значение, создаваемое для каждого HTTP-ответа. Оно появляется в политике и у намеренно добавленного элемента script. Одно значение не должно становиться постоянным паролем всей сборки.
Хэш связывает разрешение с точным содержимым скрипта. Изменение текста, включая значимые для вычисления байты пробелов, меняет результат и требует соответствующего обновления политики. Такой подход удобен для заранее известного содержимого, но не разрешает произвольные будущие изменения автоматически. OWASP отдельно предупреждает против механической вставки nonce во все найденные script: так пометку доверия может получить и внедрённый код. Смысл механизма состоит в выборе доверенного содержимого при формировании документа, а не в украшении любого уже существующего тега. Ресурс на разрешённом домене и конкретный проверенный скрипт — разные модели доверия.
Что меняет strict-dynamic
script-src охватывает внешние скрипты, встроенный код и некоторые способы исполнения, включая обработчики событий в HTML. При отсутствии script-src браузер может использовать default-src. Но разрешение внешнего файла не обязательно разрешает встроенный onclick, а хэш обычного script не следует считать автоматическим разрешением любого обработчика с похожим текстом.
strict-dynamic распространяет доверие от корневого скрипта, допущенного nonce или хэшем, на скрипты, которые он загружает. В поддерживающей реализации при этом игнорируются обычные выражения списка источников вроде self. Это существенная смена модели: решение зависит от доверенного загрузчика и его поведения, а не от независимой проверки каждого URL по прежнему списку. Добавление слова в длинный заголовок поэтому способно изменить смысл соседних выражений. Документация также показывает различия интерпретации совместимой записи между поколениями CSP. Один и тот же текст политики не гарантирует буквально одинаковый набор правил в любой старой реализации.
Кто может встроить документ
frame-ancestors отвечает за родителей, которым разрешено включать текущий документ через iframe, frame, object или embed. Это не frame-src: последняя определяет, откуда сама страница получает свои встроенные фреймы. Направление отношения принципиально различается.
Директива проверяет каждого предка в цепочке вложенного встраивания. Разрешённого непосредственного родителя недостаточно, если более высокий предок не соответствует политике. Для frame-ancestors нет резервного значения от default-src: отсутствие директивы не превращает default-src self в запрет чужого встраивания. Кроме того, она не поддерживается через meta. Такой набор условий объясняет, почему похожие внешне настройки могут оставлять страницу доступной для вложения. Защита от clickjacking ограничивает возможность поместить настоящий интерфейс внутри обманного окружения; она не делает каждую кнопку безопасной и не заменяет серверные проверки операции. Иллюстрация MDN показывает учебный пример такого наложения, а не реальный финансовый сервис.

| Директива | Что ограничивается |
|---|---|
| script-src | Источники JavaScript |
| frame-src | Источники фреймов внутри документа |
| frame-ancestors | Предки, встраивающие текущий документ |
Источник: MDN: frame-ancestors и отличие frame-src
Report-Only наблюдает, а не запрещает
Content-Security-Policy-Report-Only позволяет получать сведения о нарушениях предполагаемой политики без применения её блокировок. Это отдельный заголовок. Если ресурс успешно загрузился при таком режиме, наблюдение за нарушением ещё не означает сбоя механизма: он намеренно не запретил загрузку.
Режим нужен для сопоставления модели ограничений с реальными зависимостями документа. В отчёты могут попадать легальные внешние ресурсы, старый встроенный код или действия расширений; само событие не доказывает эксплуатацию уязвимости. Для передачи отчётов указывается reporting-директива и соответствующая настройка получателя. Report-Only нельзя перенести в meta как равноценную замену заголовка. Одновременно может существовать уже применяемая политика и наблюдаемая более строгая версия: их назначения различаются. Ошибка настройки приёмника способна оставить сервер без отчётов, но не является доказательством отсутствия нарушений. Наблюдение за политикой требует различать действие браузера, формирование сообщения и его последующую доставку.
Имя reporting endpoint не является адресом
Директива report-to содержит имя получателя. Соответствие имени и URL задаётся отдельно заголовком Reporting-Endpoints. Поэтому строка с произвольным названием в CSP без объявления адреса не создаёт рабочий сервис сбора. При нарушении формируется сериализованный объект отчёта; Reporting API отправляет сообщения POST с типом application/reports+json.
В отчёте могут присутствовать документ, эффективная директива, заблокированный URL, исходная политика и место нарушения. Эти сведения помогают различать группы проблем, но остаются диагностическими данными клиента. Их приём не должен интерпретироваться как привилегированная команда приложению. report-to задуман как замена устаревающему report-uri, однако набор поддерживаемых механизмов зависит от браузера. Документация допускает совместное объявление для охвата разных реализаций; браузер с поддержкой report-to игнорирует report-uri. Следовательно, две записи не обязательно означают две копии каждого отчёта. Это правило выбора транспорта, а не объединение независимых каналов подтверждения атаки.
Источники ресурсов и пределы самой политики
Выражения внутри одной директивы загрузки представляют альтернативы: подходящего разрешения достаточно. Значение none используется отдельно для полного запрета соответствующей категории. Широкое разрешение схемы https не равнозначно ограничению конкретным доверенным сервером: оно описывает транспорт, а не владельца ресурса.
CSP применяется браузером к конкретному документу. Она не исправляет автоматически место небезопасного формирования HTML, не проверяет права пользователя на серверный объект и не гарантирует подлинность содержимого разрешённого ресурса. При этом разные цели — ограничение исполняемого кода, источников изображений и цепочки встраивания — могут описываться одной политикой. Их объединение в заголовке сохраняет отдельный смысл каждой директивы, а не превращает их в один общий уровень «защищённости сайта».


