Что такое Content Security Policy и как её внедрить на WordPress
Content Security Policy, или CSP, — это механизм защиты браузера, который ограничивает источники скриптов, стилей, изображений, шрифтов, фреймов и других ресурсов страницы. Политика передаётся в HTTP-заголовке и сообщает браузеру, какие действия разрешены для конкретного сайта. Если вредоносный код попытается загрузиться с неизвестного адреса, браузер может заблокировать его ещё до выполнения.
Для WordPress такая защита особенно актуальна. Система использует административные скрипты, встроенный JavaScript, AJAX-запросы, внешние шрифты, видеоплееры, рекламные системы и многочисленные плагины. Поэтому простое добавление строгого заголовка часто нарушает отдельные функции сайта: пропадает редактор, перестаёт работать форма или не открывается окно авторизации.
Грамотное внедрение CSP начинается с инвентаризации ресурсов, а не с копирования готовой строки из чужой конфигурации. Нужно понять, какие домены используются темой, плагинами, аналитикой, CDN и платёжными сервисами. После этого политика вводится в режиме наблюдения, проверяется по отчётам и только затем переводится в режим блокировки.
CSP не заменяет обновления WordPress, резервное копирование, защиту учётных записей и проверку файлов. Это дополнительный уровень безопасности, который уменьшает последствия межсайтового скриптинга, внедрения вредоносного JavaScript и случайной загрузки контента из ненадёжного источника.
Как браузер применяет CSP
Политика обычно задаётся заголовком Content-Security-Policy. В нём перечисляются директивы, определяющие допустимые источники для разных типов ресурсов. Например, script-src отвечает за JavaScript, style-src — за CSS, а img-src — за изображения. Браузер анализирует каждую загрузку и сравнивает её с установленными правилами.
Минимальный вариант может выглядеть так:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'self';
Значение 'self' разрешает ресурсы с того же происхождения: протокола, домена и порта. Источник data: часто требуется для встроенных изображений и некоторых шрифтов, однако его не следует добавлять без необходимости. Директива frame-ancestors запрещает другим сайтам встраивать страницы в iframe и помогает против атак с подменой интерфейса.
Политика может быть задана и через HTML-метатег, но для полноценной защиты предпочтителен HTTP-заголовок. Метатег обрабатывается позже, не поддерживает все возможности CSP и не охватывает часть ранних запросов. Серверный заголовок также проще централизованно включать для всего сайта.
Подготовка политики для сайта
Перед настройкой следует открыть сайт в браузере, перейти в инструменты разработчика и посмотреть вкладки Console и Network. Нарушения CSP обычно отображаются с указанием заблокированного ресурса и директивы, которая не разрешила его загрузку. Полезно отдельно проверить главную страницу, записи, формы, поиск, личный кабинет, корзину и административную панель.
Составьте список внешних источников. В него могут войти www.google-analytics.com, сервисы карт, YouTube, Vimeo, CDN, платёжные шлюзы, системы комментариев и шрифты Google. Для каждого адреса нужно определить тип использования: выполнение скрипта, отправка данных, подключение шрифта, отображение изображения или загрузка фрейма. Один и тот же домен может потребоваться в нескольких директивах.
Начинать стоит с режима Content-Security-Policy-Report-Only. В этом случае браузер фиксирует нарушения и отправляет отчёты, но не блокирует ресурсы. Пример заголовка:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
На практике отчёты можно направить во внешний сервис мониторинга или обработчик на сервере. Важно отфильтровывать повторяющиеся уведомления, расширения браузера и сторонние страницы, которые не относятся к вашему сайту. Иначе журнал быстро заполнится малозначимыми сообщениями.
Основные директивы и безопасные значения
Директива default-src служит запасным правилом для типов ресурсов, которые не описаны отдельно. Обычно её задают как 'self', а затем явно добавляют script-src, style-src, img-src, font-src, connect-src, frame-src и media-src. Такой подход делает политику понятнее и снижает риск случайно разрешить лишний контент.
script-src требует особого внимания. Значение 'unsafe-inline' разрешает встроенный JavaScript и заметно ослабляет защиту от XSS. 'unsafe-eval' позволяет выполнять строки как код и также считается нежелательным исключением. Некоторые плагины WordPress без них не работают, но сначала стоит проверить обновления или заменить проблемное расширение.
Для inline-скриптов существуют nonce и хэши. Сервер создаёт случайный одноразовый токен, добавляет его в CSP и в атрибут nonce разрешённого тега script:
<script nonce="случайный-токен">/* разрешённый код */</script>
В заголовке это выглядит примерно так: script-src 'self' 'nonce-случайный-токен'. В WordPress реализовать динамический nonce можно через PHP-фильтры и корректное добавление атрибута к конкретным скриптам. Хэши удобны для неизменяемых встроенных фрагментов, но после любого изменения пробелов или символов значение придётся пересчитать.
Не стоит бездумно добавлять * в качестве источника. Такая запись разрешает загрузку почти отовсюду и сводит смысл политики к минимуму. Для изображений иногда оправдано https:, но для JavaScript лучше перечислять конкретные домены.
Настройка заголовка на сервере
На Apache заголовок можно добавить в .htaccess, если на сервере разрешён модуль mod_headers:
<IfModule mod_headers.c>
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://analytics.example; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://analytics.example; frame-src 'self' https://www.youtube.com; frame-ancestors 'self';"
</IfModule>
Перед сохранением конфигурации сделайте резервную копию файла. Ошибка в синтаксисе может вызвать ошибку сервера или привести к неожиданному поведению кэширования. Если требуется ограничить подозрительные обращения к самому WordPress, полезно отдельно изучить правила блокировки запросов, поскольку CSP и фильтрация HTTP-запросов решают разные задачи.
На Nginx используется директива add_header, обычно внутри блока server:
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'self';" always;
Параметр always позволяет добавлять заголовок и к ответам с ошибками. После изменения конфигурации нужно проверить её командой nginx -t, а затем перезагрузить Nginx. В панели хостинга аналогичная настройка может находиться в разделе дополнительных HTTP-заголовков.
Особенности WordPress и его расширений
WordPress редко работает с полностью статическими страницами. Ядро, тема и плагины могут добавлять скрипты через wp_enqueue_script, вставлять небольшие inline-фрагменты и обращаться к REST API. Поэтому директива connect-src должна учитывать адреса AJAX и API, а иногда также admin-ajax.php и wp-json. Внутренний путь сайта разрешается значением 'self', если запросы идут с того же домена.
Административную часть желательно тестировать отдельно. Политика для публичных страниц может быть строгой, но редактор блоков, загрузка медиатеки и экран настроек плагина иногда используют дополнительные inline-скрипты. Не следует ослаблять защиту для всего сайта только ради одного расширения: лучше найти точный источник проблемы, обновить плагин или ограничить исключение нужным доменом.
Сторонние сервисы нужно подключать адресно. Для YouTube может понадобиться разрешение в frame-src, для Google Fonts — в style-src и font-src, для аналитики — в script-src и connect-src. Если плагин загружает ресурсы с нескольких поддоменов, это следует подтвердить по журналу браузера, а не угадывать заранее.
Кэширование также влияет на работу CSP. Если заголовок содержит динамический nonce, страница не должна отдаваться из общего кэша со старым токеном. При использовании CDN проверьте, передаёт ли он заголовок без изменений и не объединяет ли ответы разных пользователей. Статическая политика без nonce обычно совместима с кэшированием проще.
Проверка, отчёты и постепенное ужесточение
После включения режима наблюдения проверьте сайт в нескольких браузерах и на мобильном устройстве. Откройте формы, меню, всплывающие окна, галереи, видеоплееры и кнопки оплаты. Ошибка в консоли важна только тогда, когда она относится к реальному сценарию пользователя: часть сообщений может исходить от браузерных расширений или заблокированных трекеров.
Когда список разрешённых источников стабилизируется, переключитесь на обычный Content-Security-Policy. Делать это лучше в период низкой нагрузки, сохранив предыдущую конфигурацию для быстрого отката. После включения блокировки повторно проверьте ключевые страницы и следите за обращениями посетителей.
Полезно добавить дополнительные директивы: base-uri 'self' ограничивает изменение базового URL, object-src 'none' запрещает устаревшие плагины, а form-action 'self' контролирует адреса отправки форм. Директива upgrade-insecure-requests переводит HTTP-ресурсы на HTTPS, но её следует включать только после проверки всех внешних материалов.
Политика безопасности не является разовой настройкой. При установке нового плагина, добавлении рекламного кода или смене CDN список источников может измениться. Храните заголовок в документации проекта, периодически просматривайте отчёты и удаляйте давно не используемые исключения. Так CSP остаётся рабочим инструментом, а не длинной строкой с забытыми разрешениями.
Проверьте свой WordPress в режиме Report-Only, соберите реальные нарушения и составьте список необходимых источников. Затем добавьте заголовок на тестовом окружении, протестируйте основные функции и только после этого включайте блокировку на рабочем сайте.