К основному содержимому

Content Security Policy (CSP): защита от XSS атак

Безопасность · 19.04.2026 · 4 мин чтения
Иллюстрация к статье «Content Security Policy (CSP): защита от XSS атак»

Content Security Policy (CSP) — HTTP-заголовок, который сообщает браузеру, откуда разрешено загружать скрипты, стили, изображения и другие ресурсы. Это эффективная защита от XSS-атак и data injection.

Базовые директивы CSP

ДирективаКонтролирует
default-srcИсточник по умолчанию для всех ресурсов
script-srcJavaScript
style-srcCSS
img-srcИзображения
connect-srcAJAX, WebSocket, Fetch
font-srcШрифты
frame-srciframes
report-uriURL для отчётов о нарушениях

Примеры CSP политик

Строгая политика (для простых сайтов)

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'

Для WordPress с Google Fonts и GTM

Content-Security-Policy: 
  default-src 'self';
  script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com;
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: https://www.google-analytics.com;

Настройка через .htaccess

<IfModule mod_headers.c>
    Header always set Content-Security-Policy "        default-src 'self';         script-src 'self' 'unsafe-inline' 'unsafe-eval';         style-src 'self' 'unsafe-inline';         img-src 'self' data: https:;         font-src 'self' data:;         connect-src 'self';"
</IfModule>

Режим Report-Only для тестирования

# Не блокирует, только присылает отчёты
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report.php
💡 Начинайте с Content-Security-Policy-Report-Only — собирайте нарушения неделю, затем переходите на строгий CSP без поломки сайта.
⚠️ 'unsafe-inline' и 'unsafe-eval' ослабляют защиту. Используйте nonce или hash для инлайн-скриптов в production.

Разбор сообщений о нарушении CSP

Браузер всегда печатает в консоли, какая именно директива сработала и что заблокировано — не нужно гадать.

  • Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution. — инлайн-скрипт в HTML заблокирован; решение — вынести код в отдельный файл или подписать блок nonce/hash.
  • Refused to load the stylesheet 'https://fonts.googleapis.com/...' because it violates the following Content Security Policy directive: "style-src 'self'". — домен шрифтов не добавлен в style-src, стили с внешнего CDN не подключатся.
  • Refused to connect to 'https://api.example.com/...' because it violates the following Content Security Policy directive: "connect-src 'self'". — AJAX/fetch-запрос к стороннему API заблокирован; домен нужно явно перечислить в connect-src.
  • Refused to frame 'https://widget.example.com/' because it violates the following Content Security Policy directive: "frame-src 'self'". — встроенный iframe стороннего виджета не откроется, пока его домен не появится в frame-src.
  • JSON-отчёт на report-uri вида {"csp-report": {"document-uri": "...", "violated-directive": "script-src-elem", "blocked-uri": "...", "line-number": 42}} — так браузер сообщает о нарушении на сервер: видно, какая строка кода и какой ресурс вызвали блокировку, даже если пользователь не открывал консоль.

Способы задать CSP: где и когда

СпособГде задаётсяПоддержка frame-ancestorsКогда использовать
HTTP-заголовок Content-Security-PolicyОтвет сервера (Nginx, Apache, PHP)Да, полностьюОсновной способ в production
Тег <meta http-equiv> в HTMLВнутри <head> страницыНет, директива игнорируется в meta-тегеКогда нет доступа к серверным заголовкам, например на статическом хостинге без конфигурации
Content-Security-Policy-Report-OnlyОтдельный заголовок ответа сервераДаТестовый режим: собирает нарушения, но ничего не блокирует
Одновременно enforce и report-onlyДва заголовка сразуДаПостепенное ужесточение действующей политики со сбором данных о новых нарушениях

Когда включать строгий CSP рано

  • В коде много инлайн-обработчиков вида onclick="..." и разбросанных <script> без атрибутов — строгий script-src без 'unsafe-inline' сломает их все разом; нужен постепенный перевод на nonce или hash, а не мгновенное включение.
  • На сайте стоят сторонние виджеты — чат поддержки, платёжная форма, рекламная сеть, — которые сами внедряют инлайн-код и тянут ресурсы с непредсказуемых доменов; их источники придётся вручную добавлять в политику и периодически обновлять список.
  • Сайт раздаётся через несколько CDN с часто меняющимися поддоменами — фиксированный список доменов в CSP быстро устаревает и требует постоянной ручной синхронизации.
  • Нет возможности собирать отчёты о нарушениях — включение строгой политики без периода Report-Only почти гарантированно ломает часть функциональности незаметно для администратора, пока не пожалуются пользователи.

Проверка, что политика реально применяется

# Убедиться, что заголовок действительно приходит от сервера
curl -I https://example.com | grep -i content-security-policy

# Открыть DevTools → Console на боевой странице:
# строк "Refused to" быть не должно, если политика в режиме enforce

Для проверки самой политики на логические ошибки (слишком широкие источники, отсутствие object-src) удобно вставить значение заголовка в независимый онлайн-анализатор CSP, например CSP Evaluator от Google. Если параллельно настроен report-uri, отчёты о попытках нарушения приходят даже когда политика уже в режиме enforce, а не только на этапе Report-Only.

Статья помогла?
← Назад в базу знаний Задать вопрос поддержке