Отчёт Content Security Policy показывает заблокированный инлайн-скрипт — найдите причину
Ваша страница поиска работает под Content Security Policy (CSP) на основе nonce. За ночь этот отчёт о нарушении начал приходить многократно, всегда с одинаковой формой document URI.
Ограничения:
- страница выводит поисковый запрос в заголовок результатов на клиенте
- в серверной разметке этого маршрута нет инлайновых тегов script
"csp-report":
document-uri = https://shop.example.com/search?q=...
referrer =
violated-directive = script-src-elem
blocked-uri = inline
status-code = 200
script-sample =
Определите причину и назовите исправление.
В DOM попал инлайн-скрипт без nonce, хотя маршрут таких не отдаёт, значит его внедрили на клиенте — это DOM XSS. Поисковый запрос идёт из URL в заголовок результатов через разбирающий sink. Политика заблокировала и сообщила; чинить надо sink, а не политику.
- ✗Списывать отчёты о нарушениях на шум расширений, не проверив маршрут
- ✗Заглушать отчёт через
unsafe-inlineвместо исправления sink - ✗Считать, что нарушение с
inlineозначает серверную разметку
- →Какой источник в этом потоке вы проследите первым и до какого sink?
- →Почему пустое поле
script-sampleне исключает внедрённый payload?
Что говорит отчёт
violated-directive = script-src-elem
blocked-uri = inline
status-code = 200
blocked-uri = inline означает, что в DOM оказался инлайн-скрипт без nonce. Но по условию серверная разметка этого маршрута инлайновых скриптов не содержит — значит, тег появился уже в браузере. Пустой referrer и повторяемость с одинаковым document-uri (/search?q=...) указывают на прямые заходы по подготовленным ссылкам.
Диагноз
Это DOM XSS: значение из location.search попадает в заголовок результатов через разбирающий sink.
// уязвимо: строка разбирается как разметка
const q = new URLSearchParams(location.search).get('q');
header.innerHTML = 'Результаты по запросу: ' + q;
CSP отработала как защита в глубину — она заблокировала выполнение и сообщила, но сама дыра осталась.
Исправление
const q = new URLSearchParams(location.search).get('q');
header.textContent = 'Результаты по запросу: ' + q;
✅ Чините sink, а не политику. ⚠️ Добавление unsafe-inline убрало бы отчёты и одновременно последний барьер.