Content Security Policy (CSP) — стандарт W3C для защиты от XSS и инъекций контента. Политика описывает, из каких источников разрешено загружать скрипты, стили, изображения, шрифты и другие ресурсы. Браузер блокирует всё, что не соответствует правилам.
Проверить и отладить CSP можно с помощью CSP Analyzer и Security Headers на rechecker.ru.
Архитектура CSP
CSP задаётся заголовком Content-Security-Policy или мета-тегом:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
Заголовок имеет приоритет над мета-тегом. При наличии обоих применяются оба (ограничения объединяются).
Краткое введение в настройку — в статье Content Security Policy (CSP): настройка и примеры.
Все директивы CSP Level 3
Управление загрузкой ресурсов
| Директива | Контролирует |
|---|---|
| default-src | Fallback для остальных *-src |
| script-src | JavaScript |
| style-src | CSS |
| img-src | Изображения |
| font-src | Шрифты |
| connect-src | XHR, fetch, WebSocket, EventSource |
| media-src | audio, video |
| object-src | object, embed, applet |
| frame-src | iframe, frame |
| worker-src | Web Workers, Service Workers |
| manifest-src | Web App Manifest |
| base-uri | Ограничение тега base |
| form-action | Куда можно отправлять формы |
Управление отчётами и встраиванием
| Директива | Назначение |
|---|---|
| report-uri | URL для отправки отчётов (устаревает) |
| report-to | Имя группы Report API |
| frame-ancestors | Кто может встраивать страницу в iframe |
| sandbox | Ограничения для iframe |
Ключевые слова в источниках
| Ключевое слово | Описание |
|---|---|
| 'none' | Запретить все источники |
| 'self' | Только тот же origin |
| 'unsafe-inline' | Inline-скрипты и стили (ослабляет защиту) |
| 'unsafe-eval' | eval(), new Function(), setTimeout(string) |
| 'strict-dynamic' | Доверенные скрипты могут загружать другие |
| 'unsafe-hashes' | Разрешить hash для обработчиков событий (опасно) |
Схемы источников
https://cdn.example.com — конкретный домен
https://*.example.com — все поддомены
https: — любые HTTPS (осторожно)
data: — data: URI
blob: — blob: URI
'self' — тот же origin
Nonce
Nonce — случайное значение, меняющееся при каждом запросе. Разрешает конкретные inline-скрипты и стили.
Сервер:
const nonce = crypto.randomBytes(16).toString('base64');
res.setHeader('Content-Security-Policy', `script-src 'self' 'nonce-${nonce}'`);
// Передать nonce в HTML
res.send(`<script nonce="${nonce}">...</script>`);
HTML:
<script nonce="r4nd0mV4lu3">console.log('OK');</script>
Nonce должен быть криптографически случайным и уникальным для каждого ответа.
Hash
Hash — SHA-256, SHA-384 или SHA-512 от содержимого скрипта/стиля. Подходит для статического контента.
<script>console.log('hello');</script>
Hash от console.log('hello'); (с пробелами и переносами как в HTML):
'sha256-2qX6...'
Content-Security-Policy: script-src 'self' 'sha256-2qX6...'
При изменении скрипта нужно пересчитать hash.
strict-dynamic
С 'strict-dynamic' скрипт, загруженный доверенным способом (nonce или hash), может создавать и выполнять новые скрипты. Упрощает работу с динамическими загрузчиками (например, Webpack).
Content-Security-Policy: script-src 'nonce-abc123' 'strict-dynamic'
В современных браузерах 'unsafe-inline' игнорируется при наличии 'strict-dynamic'.
Report-Only и отчёты
Режим без блокировки — CSP Report-Only: как тестировать политику без блокировки.
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
Современный вариант с Report API:
Content-Security-Policy: default-src 'self'; report-to csp-endpoint
Report-To: {"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"https://example.com/csp-report"}]}
Формат отчёта (report-uri):
{
"csp-report": {
"document-uri": "https://example.com/page",
"violated-directive": "script-src",
"blocked-uri": "https://evil.com/malicious.js",
"original-policy": "default-src 'self'"
}
}
frame-ancestors
Ограничивает, кто может встраивать страницу в iframe. Заменяет X-Frame-Options.
Content-Security-Policy: frame-ancestors 'self' https://trusted.com
frame-ancestors 'none' — полный запрет встраивания.
base-uri
Ограничивает тег <base>, который может менять базовый URL для относительных ссылок.
Content-Security-Policy: base-uri 'self'
form-action
Ограничивает URL, на которые можно отправлять формы.
Content-Security-Policy: form-action 'self' https://payment.example.com
Примеры для популярных стеков
React (Create React App / Vite)
Обычно все скрипты бандлятся. Минимальная политика:
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.example.com
WordPress
Много inline-скриптов и сторонних плагинов. Рекомендуется Report-Only, затем постепенное ужесточение. Часто требуется 'unsafe-inline' для script-src до рефакторинга.
Next.js
// next.config.js
const cspHeader = `
default-src 'self';
script-src 'self' 'unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
`;
Пошаговое внедрение
- Соберите список всех источников (CDN, аналитика, виджеты).
- Включите Report-Only с report-uri.
- Соберите отчёты 1–2 недели.
- Добавьте легитимные источники, замените inline на nonce/hash где возможно.
- Включите блокирующую политику.
- Настройте мониторинг отчётов.
Отладка
В DevTools → Console блокировки CSP выводятся с подробностями. В Network запросы к заблокированным ресурсам помечаются как (blocked:csp).
Онлайн: CSP Analyzer — разбор политики и проверка на типичные ошибки.