CSP (Content Security Policy): настройка с нуля
CSP (Content Security Policy): настройка с нуля
Content Security Policy (CSP) — это заголовок HTTP-ответа, который декларирует браузеру допустимые источники скриптов, стилей, изображений, шрифтов и других ресурсов. Корректно написанный CSP превращает XSS-уязвимость в логирование инцидента, а не в компрометацию сессии. В статье — полный цикл настройки: от Report-Only до строгого strict-dynamic с nonces.
Как работает CSP
Браузер получает заголовок Content-Security-Policy и для каждой загружаемой ресурсной директивы (script-src, style-src, img-src, font-src и т.д.) проверяет, разрешён ли источник. Неразрешённый ресурс блокируется, и опционально (если задан report-uri или report-to) отправляется CSP-отчёт в JSON. Стандарт описан в CSP Level 3 W3C Working Draft.
Основные директивы
default-src — fallback для всех fetch-директив; script-src — скрипты; style-src — стили; img-src — изображения; connect-src — fetch/XHR/WebSocket; font-src — шрифты; frame-src — iframe; frame-ancestors — замена X-Frame-Options; object-src — plugin-объекты (рекомендуется 'none'); base-uri — значение <base> (рекомендуется 'self'); form-action — атрибут action форм.
Источники: 'self', nonce, hash, strict-dynamic
'self'— текущий origin'nonce-RANDOM'— одноразовая случайная строка на запрос; скрипт с атрибутомnonce="RANDOM"разрешён'sha256-HASH'— разрешает инлайн-скрипт с этим SHA-256 хешем'strict-dynamic'— nonce/hash-скрипт может подгружать другие скрипты; упрощает работу с бандлерами'unsafe-inline','unsafe-eval'— избегайте в production
Пример строгой политики
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{NONCE}' 'strict-dynamic';
style-src 'self' 'nonce-{NONCE}';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self' https://api.example.com;
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
object-src 'none';
upgrade-insecure-requests
Nonce — 128 бит криптографически случайных данных, сгенерированный на каждый запрос. Использовать один nonce в SPA-шаблоне нельзя — это аналог 'unsafe-inline'.
Report-Only режим для отладки
Перед включением строгого CSP запустите его в режиме отчётов:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report; report-to csp-endpoint
Собирайте отчёты 2–4 недели, анализируйте, разрешайте легитимные источники, удаляйте устаревшие. Только после «тишины» в логах переключайтесь на enforce.
nginx + nonce (через ngx_http_sub_module)
set_secure_random_alphanum $cspNonce 32;
sub_filter_once off;
sub_filter 'NONCE_PLACEHOLDER' $cspNonce;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'nonce-$cspNonce' 'strict-dynamic'; style-src 'self' 'nonce-$cspNonce'; object-src 'none'; base-uri 'self'" always;
Next.js 15 middleware
import { NextResponse } from 'next/server';
export function middleware(request) {
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
const csp = `default-src 'self'; script-src 'self' 'nonce-${nonce}' 'strict-dynamic'; style-src 'self' 'nonce-${nonce}'; object-src 'none'; base-uri 'self'`;
const headers = new Headers(request.headers);
headers.set('x-nonce', nonce);
const res = NextResponse.next({ request: { headers } });
res.headers.set('Content-Security-Policy', csp);
return res;
}
Проверка и отчёты
Проверить политику: curl -I https://example.com | grep -i content-security или CSP Analyzer enterno.io — он парсит директивы, подсвечивает отсутствующие и ставит грейд A–F. Связанные заголовки — в статье HTTP Security Headers.
FAQ
Почему не работает Google Analytics? Добавьте в script-src https://www.googletagmanager.com, в connect-src — https://www.google-analytics.com.
Что с inline style="..."? CSP3 поддерживает 'unsafe-hashes' для атрибутов, но лучше вынести стили в отдельные файлы.
strict-dynamic вместо хоста? Да — он игнорирует host-allowlist для скриптов и доверяет только nonce/hash, что сильно упрощает ведение политики.
Можно ли CSP через <meta>? Технически да, но без поддержки frame-ancestors и report-uri. Только как fallback.
Вывод
Стратегия внедрения CSP: 1) собрать audit ресурсов; 2) включить Report-Only; 3) через 2–4 недели переключиться на enforce; 4) добавить мониторинг на изменение заголовка; 5) проверять через CSP Analyzer. Связанные темы: защита от XSS, SRI для CDN.
Проверьте ваш сайт прямо сейчас
Проверить →