CSP блокирует скрипты и виджеты: как найти нарушенную директиву
После изменения заголовков безопасности сайт может продолжать открываться, но карта, форма или платёжный виджет перестают запускаться. В Console появляется сообщение о Content Security Policy, а посетитель видит пустой блок либо кнопку без результата. Такая ситуация требует проверки конкретного ограничения: необходимо установить, какой ресурс нужен сценарию, почему браузер его заблокировал и какое правило действительно следует изменить.
Если Content Security Policy блокирует скрипт, отключение всей политики быстро скрывает симптом, но одновременно меняет защиту остальных страниц. Рабочая последовательность точнее: воспроизвести действие, прочитать фактически полученную CSP, найти нарушенную директиву, согласовать необходимый источник и проверить исправление. Ниже — инструкция для владельца сайта и разработчика с учебным примером, правилами nonce и hash и критериями приёмки.
Убедитесь, что действие остановила именно CSP
Откройте DevTools до воспроизведения проблемы, перейдите в Console и повторите один конкретный шаг: например, открытие формы записи. Сохраните адрес страницы, время, версию браузера и точное сообщение. Формулировка Refused to load the script вместе с упоминанием нарушенной директивы указывает направление поиска. Для соединения или встроенного кода текст сообщения будет другим.
Красная строка не обязательно объясняет неработающую кнопку. Нарушение может относиться к необязательной аналитике, расширению браузера или другому фрейму. Сопоставьте сообщение с моментом действия, адресом заблокированного ресурса и компонентом, который перестал работать. Если виджет открывается внутри отдельного документа, проверяйте также контекст этого документа: его ограничения могут отличаться от политики основной страницы.
Зафиксируйте признак фактической блокировки. Сообщение от политики Report-Only само по себе не означает, что браузер запретил действие. Также отделите CSP от CORS, неподходящего MIME-типа и ошибки JavaScript: одинаковый результат «форма не появилась» может иметь разные причины. Изменение CSP не исправит неверный адрес файла или исключение в обработчике кнопки.
Для сравнения повторите сценарий в чистом профиле браузера с теми же правами и данными. Это помогает проверить влияние расширений. Само исчезновение ошибки в другом профиле ещё не объясняет причину: различаться могут авторизация, cookies, эксперимент или версия страницы.
Найдите политику, которую действительно получил браузер
В Network выберите запрос HTML-документа и посмотрите заголовки ответа. Сохраните значения Content-Security-Policy и, если он есть, Content-Security-Policy-Report-Only. Для обычной страницы правила загрузки её ресурсов ищут прежде всего здесь. Просмотр только заголовков заблокированного файла JavaScript может увести расследование от политики документа, который пытается его подключить.
Проверьте также наличие CSP через meta в HTML. Настройки могут приходить из приложения, веб-сервера, CDN или защитного прокси. Файл конфигурации показывает намерение команды; заголовок конкретного ответа показывает результат всей цепочки. Сравните проблемный маршрут с работающим, включая страницы после авторизации и документы, отданные из кеша.
Справочник MDN по Content-Security-Policy поясняет поведение нескольких политик: одновременно применяемые ограничивающие политики не объединяются в общий разрешающий список. Ресурс должен пройти каждую из них. Поэтому дополнительный заголовок с разрешённым доменом не отменит запрет, который продолжает отправлять другой уровень.
Назначьте одно понятное место управления согласованным набором правил либо явно опишите распределение ответственности. Если приложение изменили, а CDN продолжает добавлять прежний запрет, повторное редактирование приложения не решит проблему. Перед изменением сохраните исходные значения и маршрут доставки, чтобы можно было восстановить предыдущую конфигурацию без догадок.
Прочитайте имя нарушенной директивы и её запасное правило
Название директивы связывает сообщение браузера с типом действия. Не добавляйте все адреса виджета сразу в default-src: это расширяет разрешения сразу для нескольких видов ресурсов, использующих его как запасное правило. Сначала установите, какой именно запрос или способ выполнения заблокирован.
| Директива | Что проверять в данном сценарии |
|---|---|
script-src-elem | Подключение файла или блока кода через элемент script |
script-src-attr | Встроенный обработчик события, например атрибут onclick |
connect-src | Соединение, создаваемое JavaScript, включая запрос к API |
frame-src | Документ, который страница загружает во вложенный фрейм |
style-src или img-src | Стили или изображения, необходимые компоненту |
По документации script-src-elem, при отсутствии этой директивы браузер обращается к script-src, а затем — к default-src. Если script-src-elem уже задана явно, добавление домена только в script-src может не изменить результат для соответствующего элемента. Смотрите всю цепочку, указанную в сообщении.
Различайте загрузку фрейма и разрешение другим сайтам встраивать вашу страницу: это разные задачи. Также отделяйте внешний файл от встроенного обработчика. Разрешённый домен JavaScript не становится автоматическим разрешением на любой текст в атрибуте onclick. Правка должна соответствовать фактическому механизму работы компонента.
Соберите карту необходимых ресурсов виджета
Один внешний компонент часто загружает несколько ресурсов: основной скрипт, стили, изображения, вложенный документ и данные API. Составьте короткую таблицу «назначение — адрес — тип действия — требуемая директива». Основанием служат актуальная документация выбранного поставщика и наблюдения за нужным сценарием, а не список всех доменов, встретившихся в Console.
Отделите обязательные ресурсы от необязательной телеметрии и дополнительных функций. Например, форме записи может быть необходим API расписания, но не рекламный пиксель. Решение об этом принимает владелец интеграции вместе с разработчиком. Разрешать неизвестный источник только потому, что браузер сообщил о запрете, означает принимать запрос без проверки его назначения.
Учитывайте отложенную загрузку. Начальный экран не покажет ресурсы, которые появляются после выбора города, открытия календаря или подтверждения согласия. Пройдите согласованные варианты формы и зафиксируйте различия. Для платежей и других действий с последствиями используйте предусмотренную тестовую среду либо контролируемый сценарий без повторного реального списания.
В карточке интеграции сохраните ответственного, версию компонента и причину каждого исключения. Такой список пригодится после обновления поставщика: команда сможет проверить новый запрос и понять, является ли он ожидаемым изменением. Он также позволит удалить разрешение после отключения виджета, вместо накопления давно не используемых доменов.
Исправьте разрешение для внешнего скрипта
Предположим, учебная страница использует простой список разрешённых источников, а файл необходимого виджета расположен на https://widgets.example. В исходном фрагменте действует script-src 'self', отдельной script-src-elem нет. После проверки поставщика соответствующий фрагмент можно расширить следующим образом:
script-src 'self' https://widgets.example;
Это фрагмент для объяснения, а домен .example условный. Он не заменяет полную политику сайта. Остальные ограничения сохраняют; выбранный источник подставляют после проверки. В политике с nonce, hash или 'strict-dynamic' решение может быть другим: пример со списком доменов нельзя механически переносить в такую конфигурацию.
Проверьте точный адрес и цепочку перенаправлений. Похожее название поддомена не означает тот же источник. Не расширяйте исключение до всех доменов или всех HTTPS-ресурсов ради одного файла. Разрешённый внешний поставщик получает возможность выполнять подключаемый код в предусмотренном контексте, поэтому его необходимость и состав загружаемых зависимостей заслуживают отдельной проверки.
После правки заново получите документ и убедитесь, что браузер видит нужное правило. Если сообщение осталось, сначала сравните эффективную директиву, конечный адрес и вторую применяемую политику. Повторное добавление того же домена в разные места без понимания причины создаёт трудную для сопровождения конфигурацию.
Проверьте запросы, которые выполняет уже загруженный код
Скрипт может успешно загрузиться, нарисовать оболочку формы и остановиться при запросе расписания. Директива connect-src регулирует соединения через интерфейсы JavaScript, включая fetch, XMLHttpRequest и WebSocket. Разрешение домена в script-src не даёт ему автоматически разрешения в этой категории.
Для учебного API на отдельном условном адресе и существующего правила connect-src 'self' изменение соответствующего фрагмента выглядит так:
connect-src 'self' https://api.widgets.example;
Сначала подтвердите, что запрос нужен функции и направлен в ожидаемое окружение. Ошибка конфигурации виджета может отправлять данные на старый тестовый адрес. В таком случае исправляют адрес, а не расширяют политику рабочего сайта. Для соединения WebSocket проверяют фактическую схему и адрес, вместо предположения, что любая разрешённая HTTPS-загрузка покрывает все способы связи.
После устранения CSP-блокировки запрос может встретить отдельную проблему CORS, авторизации или самого API. Это следующий результат диагностики, а не доказательство бесполезности предыдущего исправления. Запишите, какой барьер снят и на каком шаге теперь останавливается сценарий. Готовность определяют по полученному расписанию и результату операции, а не только по исчезновению одной строки Console.
Согласуйте nonce между заголовком и HTML
Если заблокирован доверенный встроенный код инициализации, проверьте используемый способ его разрешения. В руководстве MDN по CSP описан nonce: непредсказуемое значение, которое сервер создаёт заново для каждого ответа и помещает в политику и соответствующий атрибут разрешённых элементов. Значения должны совпадать внутри данного документа.
Постоянная строка из примера не подходит для рабочего сайта. Генерацию поручают криптографически надёжному механизму используемой платформы. Также нельзя автоматически добавлять nonce ко всем обнаруженным блокам: разрешение предназначено коду, который приложение намеренно считает доверенным. Если в документ попал посторонний блок, массовое добавление атрибута способно разрешить и его.
Особенно внимательно проверьте кеширование HTML. Если заголовок сформирован заново, а тело пришло из прежнего кеша, значения могут разойтись. Простое закрепление nonce навсегда устраняет несовпадение ценой нарушения его назначения. Нужна согласованная схема формирования и доставки документа, которую проверяют повторными загрузками и на разных обслуживающих узлах.
В отчёте достаточно подтвердить согласованность заголовка и доверенного элемента. Сам атрибут не разрешает произвольный обработчик в onclick: для него действует другой механизм. Не переносите решение для элемента script на любую строку JavaScript в HTML.
Проверьте hash и существующую модель доверия
Для неизменяемого встроенного блока можно использовать разрешение по hash. Документация script-src указывает, что хешируют содержимое блока без окружающих тегов; регистр и пробельные символы имеют значение. Поэтому изменение шаблона или минификация после вычисления hash могут привести к блокировке знакомого кода.
Свяжите генерацию значения с окончательным содержимым публикуемого документа. Если шаблон подставляет разные данные в код при каждом ответе, стабильный hash требует иной организации такого блока. Не добавляйте предложенный браузером hash без проверки исходного кода: сначала подтвердите, что именно этот текст должен выполняться, и только затем оформляйте разрешение.
Если политика уже использует 'strict-dynamic', учтите передачу доверия от разрешённого через nonce или hash корневого скрипта к загружаемым им скриптам. В поддерживающих этот механизм браузерах обычные списки хостов в соответствующей директиве не работают как самостоятельное разрешение. Для такого проекта проверяют доверенный загрузчик и его способ подключения зависимостей.
Сообщения о запрещённом inline-коде и выполнении строк требуют разных решений. Не добавляйте 'unsafe-inline' или 'unsafe-eval' как универсальное средство ремонта виджета. Сначала найдите поддерживаемую поставщиком сборку и способ интеграции, совместимые с принятой моделью защиты.
Используйте Report-Only для проверки кандидата
Content-Security-Policy-Report-Only позволяет наблюдать нарушения проверяемых правил без применения их запретов. Этот режим передают HTTP-заголовком; вариант через meta не поддерживается. Для централизованного сбора сообщений необходимы настроенный приёмник и механизм отправки отчётов.
Ниже показана учебная пара заголовков для наблюдения за соединениями. Адрес приёмника условный: его требуется реализовать и проверить, а не только вписать в конфигурацию.
Reporting-Endpoints: csp="https://shop.example/csp-reports"
Content-Security-Policy-Report-Only: connect-src 'self' https://api.widgets.example; report-to csp
Поддержка способов доставки отчётов различается между браузерами. Проверьте получение контрольного сообщения в используемом окружении и сопоставляйте отчёты с воспроизведением. Отсутствие сообщений само по себе не подтверждает отсутствие нарушений. Также определите доступ к приёмнику и сроки хранения диагностических данных.
Действующая блокирующая CSP при этом сохраняет силу. Более разрешающий кандидат в Report-Only не запустит виджет, который запрещён текущей политикой. Следовательно, таким способом нельзя полностью проверить дальнейшие запросы ещё не запустившегося компонента. Полный сценарий проверяют в подготовленной среде с кандидатом в режиме применения, после чего выпускают согласованную правку.
Учебный разбор: форма записи перестала показывать время
Представим вымышленный сервис записи. После усиления заголовков основная страница отвечает успешно, но форма не открывается. Команда подтверждает, что нужный скрипт поставщика отсутствует в разрешённых источниках. Начальная конфигурация здесь использует простые списки доменов; встроенный код и модель nonce в этом примере не участвуют.
| Этап | Наблюдение | Следующее действие |
|---|---|---|
| Исходная проблема | Блокируется файл виджета по script-src | Проверить источник и изменить соответствующее правило |
| После первой правки | Форма появилась, соединение с API запрещено по connect-src | Подтвердить назначение API и согласовать отдельное разрешение |
| После второй правки | Время доступно для выбора | Проверить завершение записи в контролируемом сценарии |
| Приёмка | Операция завершена, посторонние источники остаются запрещены | Зафиксировать результат и выпустить согласованную конфигурацию |
Появление второй ошибки не означает, что первое разрешение было неправильным: ранее код не доходил до обращения к API. Именно поэтому полезно проходить цепочку последовательно и фиксировать изменение поведения. Если после разрешения соединения API возвращает отказ по собственной логике, его разбирают отдельно, сохраняя достигнутую ясность.
Владелец сайта принимает результат по возможности выбрать время и завершить тестовую запись, а разработчик подтверждает ограниченность правки. Успешная загрузка одного файла служит промежуточным свидетельством. Она не заменяет проверку всей пользовательской задачи и сохранения предусмотренных ограничений.
Проверьте работу функции и сохранение запретов
Проверка исправления включает два направления: нужная функция работает, а постороннее разрешение не появилось. Согласуйте перечень страниц и действий до выпуска. Для формы это могут быть открытие, выбор параметров, обработка неверного ввода и подтверждение результата. Важно пройти состояния, которые действительно загружают дополнительные ресурсы.
- Повторите сценарий после новой загрузки документа и подтвердите фактические заголовки ответа.
- Проверьте нужные роли, язык и вариант согласия, если они меняют состав виджета.
- Сравните исходную и итоговую политику: изменение должно соответствовать согласованной карте ресурсов.
- В тестовой среде подтвердите сохранение нужного запрета на заранее выбранном контролируемом ресурсе вне списка разрешений.
- Проверьте другие важные страницы с тем же шаблоном и заголовком.
- Убедитесь, что диагностика относится к опубликованной версии, а не к локальному обходу в DevTools.
Не требуйте полного отсутствия любых сообщений Console как единственного критерия. Там могут оставаться диагностические записи, не связанные с изменением. Требуйте объяснения релевантных нарушений и доказательства завершения выбранного сценария. При использовании Report-Only различайте сообщения наблюдаемой политики и реальную блокировку действующей.
Сохраните краткий протокол: проверенная сборка, исходная причина, изменённая директива, разрешённый источник и результат каждого сценария. Такой документ позволяет следующему специалисту понять назначение исключения и быстро проверить его после обновления интеграции.
Организуйте выпуск и дальнейшее наблюдение
Изменение CSP проходит тот же управляемый процесс, что и изменение кода: версия конфигурации, проверенная среда, ответственный и понятный способ возврата. Выпускайте согласованный набор правил через штатное место настройки. Если заголовки меняются на нескольких уровнях, заранее определите порядок обновления и проверьте результат снаружи после завершения.
При необходимости очистки кеша ограничьте её затронутыми документами и подтвердите получение новой политики. Возврат к прежним заголовкам также должен учитывать кеширование: сохранённый файл конфигурации не доказывает, что посетители уже получают предыдущую версию. Отдельно оцените, совместима ли она с одновременно обновлённым виджетом.
После выпуска наблюдайте за доступностью страниц и повторите важный браузерный сценарий. Внешний HTTP-ответ и работа JavaScript проверяют разные стороны результата. Стабильная доступность помогает заметить общую проблему, а успешная запись, отправка формы или загрузка нужного виджета подтверждают конкретную функцию.
Для внешнего контроля HTTP-доступности страниц используйте SiteGuard. Работу виджетов и сохранение ограничений CSP проверяйте в браузере силами команды.