CSP
Политика безопасности содержимого (CSP)
Политика безопасности содержимого (CSP) — это функция, которая помогает предотвратить или свести к минимуму риск определённых типов угроз безопасности. Она состоит из набора инструкций, которые веб-сайт передаёт браузеру и которые предписывают браузеру ограничить действия, доступные коду, составляющему сайт.
Основное применение CSP — контроль ресурсов, которые разрешено загружать документу, в частности ресурсов JavaScript. Это главным образом используется для защиты от атак типа межсайтового скриптинга (XSS), при которых злоумышленник может внедрить вредоносный код на сайт жертвы.
CSP может служить и другим целям, в том числе защищать от кликджекинга и помогать гарантировать, что страницы сайта будут загружаться по HTTPS.
В этом руководстве сначала описано, как CSP передаётся браузеру и как она выглядит на общем уровне.
Затем мы расскажем, как с её помощью можно:
- Контролировать загружаемые ресурсы, чтобы защититься от XSS.
- Ограничивать встраивание, чтобы защититься от кликджекинга.
- Обновлять небезопасные запросы, чтобы обеспечить передачу всех ресурсов по HTTPS.
- Требовать использования доверенных типов, чтобы помочь защититься от клиентского XSS.
Обратите внимание: между различными вариантами использования нет зависимости. Если вы хотите защититься от кликджекинга, но не от XSS, достаточно добавить директивы для соответствующего варианта использования.
Наконец, мы опишем стратегии развёртывания CSP и инструменты, которые помогут упростить этот процесс.
Обзор CSP
CSP следует передавать браузеру в заголовке ответа Content-Security-Policy. Его следует задавать для всех ответов на все запросы, а не только для основного документа.
Также можно указать CSP с помощью атрибута http-equiv элемента <meta> документа. Это полезный вариант для некоторых сценариев, например для одностраничного приложения SPA с клиентской отрисовкой, использующего только статические ресурсы, поскольку при этом можно не полагаться на серверную инфраструктуру. Однако этот вариант поддерживает не все возможности CSP.
Политика задаётся набором директив, разделённых точками с запятой. Каждая директива управляет отдельным аспектом политики безопасности. Директива состоит из имени, за которым следует пробел и значение. Синтаксис разных директив может различаться.
Например, рассмотрим следующую CSP:
Content-Security-Policy: default-src 'self'; img-src 'self' example.com
В ней заданы две директивы:
- директиве
default-srcзадано значение'self' - директиве
img-srcзадано значение'self' example.com.
Первая директива, default-src, указывает браузеру загружать только ресурсы с того же источника, что и документ, если только другие, более конкретные директивы не задают иную политику для других типов ресурсов. Вторая директива, img-src, указывает браузеру загружать изображения с того же источника или с example.com.
В следующем разделе мы рассмотрим инструменты для контроля загрузки ресурсов — основную функцию CSP.
Контроль загрузки ресурсов
CSP можно использовать для управления ресурсами, которые разрешено загружать документу. В первую очередь это используется для защиты от атак типа межсайтового скриптинга (XSS).
В этом разделе мы сначала рассмотрим, как контроль загрузки ресурсов помогает защититься от XSS, а затем — какие средства CSP предоставляет для контроля загружаемых ресурсов. Наконец, мы опишем одну конкретную рекомендуемую стратегию, которая называется «строгая CSP».
XSS и загрузка ресурсов
Атака типа межсайтового скриптинга (XSS) — это атака, при которой злоумышленник может выполнить свой код в контексте целевого веб-сайта. Этот код получает возможность делать всё, что может делать собственный код сайта, в том числе, например:
- получать доступ к содержимому загруженных страниц сайта или изменять его
- получать доступ к содержимому локального хранилища или изменять его
- отправлять HTTP-запросы с учётными данными пользователя, что позволяет выдавать себя за пользователя или получать доступ к конфиденциальным данным
XSS-атака возможна, если веб-сайт принимает вводимые данные, которые могли быть подготовлены злоумышленником (например, параметры URL или комментарий к записи в блоге), а затем включает их в страницу, не выполнив их очистку, то есть не убедившись, что эти данные нельзя выполнить как JavaScript.
Веб-сайты должны защищаться от XSS, очищая такие данные перед добавлением их на страницу.
Примечание: CSP действительно может помочь защититься от XSS двумя разными способами:
- Она помогает гарантировать, что данные очищаются перед использованием на стороне клиента: это подробнее рассматривается далее в разделе Требование доверенных типов.
- Контролируя загрузку ресурсов, CSP обеспечивает дополнительный уровень защиты от XSS, защищая веб-сайт, даже если очистка не удалась. Именно эту защиту от XSS мы рассмотрим в этом разделе.
Если очистка не удалась, внедрённый вредоносный код может принимать в документе различные формы, в том числе:
-
Тег
<script>, ссылающийся на вредоносный источник:<script src="https://evil.example.com/hacker.js"></script>
-
Тег
<script>со встроенным JavaScript:<script> console.log("You've been hacked!"); </script> -
Встроенный обработчик события:
<img onmouseover="console.log(`You've been hacked!`)" src="thumbnail.jpg" alt="" />
-
URL
javascript::<iframe src="javascript:console.log(`You've been hacked!`)"></iframe>
-
Строковый аргумент небезопасного API, например
eval():eval("console.log(`You've been hacked!`)");
Контролируя загрузку ресурсов, CSP может обеспечить защиту от всех этих видов кода. С помощью CSP можно:
- определить разрешённые источники файлов JavaScript и других ресурсов, фактически блокируя загрузку с
https://evil.example.com - запретить встроенные теги скриптов
- разрешить только теги скриптов с правильным значением nonce или хешем
- запретить встроенные обработчики событий
- запретить URL
javascript: - запретить опасные API, например
eval()
В следующем разделе мы рассмотрим средства CSP, позволяющие это делать.
Примечание: Настройка CSP не заменяет очистку данных. Веб-сайты должны очищать данные и задавать CSP, обеспечивая многоуровневую защиту от XSS.
Директивы загрузки
Директивы загрузки используются для задания категорий ресурсов, которые разрешено загружать документу, например JavaScript, таблиц стилей CSS, изображений, шрифтов и так далее.
Для разных типов ресурсов предусмотрены разные директивы загрузки. Например:
-
script-srcзадаёт разрешённые источники JavaScript. -
style-srcзадаёт разрешённые источники таблиц стилей CSS. -
img-srcзадаёт разрешённые источники изображений.
Особая директива загрузки — default-src. Она задаёт резервную политику для всех ресурсов, директивы которых явно не перечислены.
Полный список директив загрузки см. в справочной документации.
Для каждой директивы загрузки можно указать либо единственное ключевое слово 'none', либо одно или несколько выражений источника, разделённых пробелами. Если указано несколько выражений источника, ресурс разрешается, если хотя бы один из способов разрешает его загрузку.
Например, приведённая ниже CSP задаёт две директивы загрузки:
-
для
default-srcуказано единственное выражение источника'self' -
для
img-srcуказаны два выражения источника:'self'иexample.com
В результате:
- изображения должны находиться на том же источнике, что и документ, либо загружаться с
example.com - все остальные ресурсы должны находиться на том же источнике, что и документ.
В следующих нескольких разделах мы опишем некоторые способы управления загрузкой ресурсов с помощью выражений источника. Хотя мы рассматриваем их по отдельности, в общем случае эти выражения можно сочетать: например, одна директива загрузки может включать как nonce, так и имена хостов.
Блокировка ресурсов
Чтобы полностью заблокировать тип ресурсов, используйте ключевое слово 'none'. Например, следующая директива блокирует все ресурсы <object> и <embed>:
Content-Security-Policy: object-src 'none'
Обратите внимание: 'none' нельзя комбинировать с другими способами в одной директиве. На практике, если вместе с 'none' указаны другие выражения источника, они игнорируются.
Nonce
Использование nonce — рекомендуемый способ ограничить загрузку ресурсов <script> и <style>.
При использовании nonce сервер генерирует случайное значение для каждого HTTP-ответа и включает его в директиву script-src и/или style-src:
Content-Security-Policy: script-src 'nonce-416d1177-4d12-4e3b-b7c9-f6c409789fb8'
Затем сервер включает это значение в качестве значения атрибута nonce всех тегов <script> и/или <style>, которые он намерен добавить в документ.
Браузер сравнивает эти два значения и загружает ресурс, только если они совпадают. Идея заключается в том, что даже если злоумышленник сможет внедрить в страницу JavaScript, он не будет знать, какой nonce использует сервер, поэтому браузер откажется выполнять скрипт.
Чтобы этот подход работал, злоумышленник не должен иметь возможности угадать nonce.
На практике это означает, что nonce должен быть уникальным для каждого HTTP-ответа и непредсказуемым.
Это, в свою очередь, означает, что сервер не может отдавать статический HTML, поскольку при каждом запросе он должен добавлять новый nonce. Обычно для добавления nonce сервер использует шаблонизатор.
Вот фрагмент кода Express в качестве примера:
function content(nonce) {
return `
<script nonce="${nonce}" src="/main.js"></script>
<script nonce="${nonce}">console.log("hello!");</script>
<h1>Hello world</h1>
`;
}
app.get("/", (req, res) => {
const nonce = crypto.randomUUID();
res.setHeader("Content-Security-Policy", `script-src 'nonce-${nonce}'`);
res.send(content(nonce));
});
При каждом запросе сервер генерирует новый nonce и добавляет его в CSP и теги <script> в возвращаемом документе. Обратите внимание, что сервер:
- генерирует новый nonce для каждого запроса
- может использовать nonce как для внешних, так и для встроенных скриптов
- использует один и тот же nonce для всех тегов
<script>в документе
Важно, чтобы сервер использовал какой-либо шаблонизатор для добавления nonce, а не просто добавлял его во все теги <script>: иначе сервер может случайно добавить nonce к скриптам, внедрённым злоумышленником.
Обратите внимание: nonce можно использовать только для элементов с атрибутом nonce, то есть только для элементов <script> и <style>.
Хеши
В директивах загрузки также можно использовать хеш скрипта, чтобы гарантировать его целостность. При этом сервер:
- вычисляет хеш содержимого скрипта с помощью хеш-функции (SHA-256, SHA-384 или SHA-512)
- кодирует результат в формате Base64
- добавляет префикс, указывающий использованный алгоритм хеширования (
sha256-,sha384-илиsha512-).
Затем результат добавляется в директиву:
Content-Security-Policy: script-src 'sha256-cd9827ad...'
Получив документ, браузер вычисляет хеш скрипта, сравнивает результат со значением из заголовка и загружает скрипт, только если значения совпадают.
Чтобы этот метод работал, внешние скрипты также должны содержать атрибут integrity.
Вот пример фрагмента кода Express:
const hash1 = "sha256-ex2O7MWOzfczthhKm6azheryNVoERSFrPrdvxRtP8DI=";
const hash2 = "sha256-H/eahVJiG1zBXPQyXX0V6oaxkfiBdmanvfG9eZWSuEc=";
const csp = `script-src '${hash1}' '${hash2}'`;
const content = `
<script src="./main.js" integrity="${hash2}"></script>
<script>console.log("hello!");</script>
<h1>Hello world</h1>
`;
app.get("/", (req, res) => {
res.setHeader("Content-Security-Policy", csp);
res.send(content);
});
Обратите внимание:
- Для каждого скрипта в документе используется отдельный хеш.
- Для внешнего скрипта «main.js» также добавляется атрибут
integrityс тем же значением. - В отличие от примера с nonce, и CSP, и содержимое могут быть статическими, поскольку хеши не меняются. Поэтому политики на основе хешей лучше подходят для статических страниц или веб-сайтов, использующих клиентскую отрисовку.
Политики на основе схемы
В директивах загрузки можно указать схему, например https:, чтобы разрешить ресурсы, передаваемые с использованием этой схемы. Например, так можно потребовать использование HTTPS для загрузки всех ресурсов:
Content-Security-Policy: default-src https:
Политики на основе расположения
Директивы загрузки позволяют управлять загрузкой ресурсов в зависимости от их расположения.
Ключевое слово 'self' разрешает ресурсы с того же источника, что и сам документ:
Content-Security-Policy: img-src 'self'
Также можно указать одно или несколько имён хостов, в том числе с подстановочными символами. Тогда будут разрешены только ресурсы с этих хостов. Например, так можно разрешить доставку содержимого с доверенной CDN.
Content-Security-Policy: img-src *.example.org
Можно указать несколько местоположений. Следующая директива разрешает только изображения с того же источника, что и текущий документ, с поддомена «example.org» или с «example.com»:
Content-Security-Policy: img-src 'self' *.example.org example.com
Встроенный JavaScript
Если CSP содержит директиву default-src или script-src, выполнение встроенного JavaScript запрещается, если не принять дополнительные меры для его разрешения. К нему относятся:
-
JavaScript, включённый в элемент
<script>страницы:<script> console.log("Hello from an inline script"); </script> -
JavaScript во встроенном атрибуте обработчика события:
<img src="x" onerror="console.log('Hello from an inline event handler')" /> -
JavaScript в URL
javascript::<a href="javascript:console.log('Hello from a javascript: URL')">Click me</a>
Ключевое слово unsafe-inline можно использовать, чтобы снять это ограничение. Например, следующая директива требует, чтобы все ресурсы находились на том же источнике, но разрешает встроенный JavaScript:
Content-Security-Policy: default-src 'self' 'unsafe-inline'
Предупреждение: Разработчикам следует избегать 'unsafe-inline', поскольку это сводит на нет большую часть смысла CSP. Встроенный JavaScript — один из наиболее распространённых способов проведения XSS-атак, а одна из основных целей CSP — предотвращать его неконтролируемое использование.
Встроенные элементы <script> разрешены, если они защищены nonce или хешем, как описано выше.
Если директива содержит выражения nonce или хеша, браузеры игнорируют ключевое слово unsafe-inline.
eval() и похожие API
Как и в случае со встроенным JavaScript, если CSP содержит директиву default-src или script-src, выполнение eval() и похожих API запрещается. К ним относятся, помимо прочего:
-
сам
eval():eval('console.log("hello from eval()")'); -
конструктор
Function():const sum = new Function("a", "b", "return a + b"); -
строковый аргумент функций
setTimeout()иsetInterval():setTimeout("console.log('hello from setTimeout')", 1);
Ключевое слово unsafe-eval можно использовать, чтобы изменить это поведение. Как и в случае с unsafe-inline, по тем же причинам разработчикам следует избегать unsafe-eval.
Иногда бывает трудно удалить использование eval() и других методов. В таких случаях API Trusted Types может повысить безопасность, гарантируя, что вводимые данные соответствуют заданной политике. В этом случае для изменения поведения следует использовать ключевое слово trusted-types-eval. В отличие от unsafe-inline, оно изменяет поведение браузера только при поддержке и включении доверенных типов, что гарантирует блокировку этих методов в браузерах, не поддерживающих доверенные типы.
В отличие от unsafe-inline, ключевое слово unsafe-eval действует и в директиве, содержащей выражения nonce или хеша.
Строгая CSP
Чтобы контролировать загрузку скриптов и снизить риск XSS, рекомендуется использовать директивы загрузки на основе nonce или хешей. Такая политика называется строгой CSP. У этого типа CSP есть два основных преимущества перед CSP на основе расположения (обычно называемой CSP со списком разрешённых источников):
- Списки разрешённых источников сложно составить правильно; часто в политику случайно вносят небезопасные домены, и поэтому она не обеспечивает эффективную защиту от XSS (см. статью CSP мертва, да здравствует CSP! О небезопасности списков разрешённых источников и будущем политики безопасности содержимого).
- Списки разрешённых источников могут быть очень большими и сложными в обслуживании, особенно при использовании скриптов, которые вам не подконтрольны. Согласно статье Как я перестал беспокоиться и полюбил политику безопасности содержимого, для подключения Google Analytics разработчику предлагают добавить в список разрешённых источников 187 доменов Google.
Строгая CSP на основе nonce выглядит так:
Content-Security-Policy:
script-src 'nonce-{RANDOM}';
object-src 'none';
base-uri 'none';
В этой CSP мы:
- используем nonce для контроля разрешённых к загрузке ресурсов JavaScript
- блокируем все встраиваемые объекты
- блокируем все случаи использования элемента
<base>для задания базового URI.
Строгая CSP на основе хешей устроена так же, но вместо nonce в ней используются хеши:
Content-Security-Policy:
script-src 'sha256-{HASHED_SCRIPT}';
object-src 'none';
base-uri 'none';
Директивы на основе nonce проще обслуживать, если вы можете динамически генерировать ответы, включая само содержимое. В противном случае необходимо использовать директивы на основе хешей. Недостаток директив на основе хешей заключается в том, что при любом изменении содержимого скрипта хеш необходимо пересчитать и обновить.
Ключевое слово strict-dynamic
Как описано выше, строгую CSP трудно реализовать при использовании скриптов, которые вам не подконтрольны. Если сторонний скрипт загружает дополнительные скрипты или использует встроенные скрипты, это не сработает, поскольку сторонний скрипт не передаст им nonce или хеш.
Для решения этой проблемы предусмотрено ключевое слово strict-dynamic. Его можно включить в директиву загрузки. Если скрипту задан nonce или хеш, этот скрипт сможет загружать другие скрипты, у которых нет собственных nonce или хешей. Иными словами, доверие к скрипту, основанное на nonce или хеше, распространяется на скрипты, загружаемые исходным скриптом (а также на скрипты, загружаемые ими, и так далее).
Например, рассмотрим такой документ:
<html lang="en-US">
<head>
<script
src="./main.js"
integrity="sha256-gEh1+8U9S1vkEuQSmmUMTZjyNSu5tIoECP4UXIEjMTk="></script>
</head>
<body>
<h1>Example page!</h1>
</body>
</html>
В нём подключён скрипт «main.js», который создаёт и добавляет другой скрипт — «main2.js»:
console.log("hello");
const scriptElement = document.createElement("script");
scriptElement.src = `main2.js`;
document.head.appendChild(scriptElement);
Мы отдаём документ с такой CSP:
Content-Security-Policy: script-src 'sha256-gEh1+8U9S1vkEuQSmmUMTZjyNSu5tIoECP4UXIEjMTk='
Скрипт «main.js» сможет загрузиться, поскольку его хеш совпадает со значением в CSP. Но попытка загрузить «main2.js» завершится неудачей.
Если добавить в CSP 'strict-dynamic', скрипт «main.js» сможет загрузить «main2.js»:
Content-Security-Policy: script-src 'sha256-gEh1+8U9S1vkEuQSmmUMTZjyNSu5tIoECP4UXIEjMTk=' 'strict-dynamic'
Ключевое слово 'strict-dynamic' значительно упрощает создание и обслуживание CSP на основе nonce или хешей, особенно если веб-сайт использует сторонние скрипты. Однако оно снижает безопасность CSP, поскольку если подключаемые скрипты создают элементы <script> на основе потенциальных источников XSS, CSP не защитит от них.
Переработка встроенного JavaScript и eval()
Как мы уже выяснили, по умолчанию CSP запрещает встроенный JavaScript. С помощью nonce или хешей разработчик может использовать встроенные теги <script>, но всё равно потребуется переработать код, чтобы удалить другие запрещённые конструкции, в том числе встроенные обработчики событий, URL javascript: и использование eval(). Например, встроенные обработчики событий обычно следует заменить вызовами addEventListener():
<p onclick="console.log('Hello from an inline event handler')">click me</p>
<!-- served with the following CSP:
`script-src 'sha256-AjYfua7yQhrSlg807yyeaggxQ7rP9Lu0Odz7MZv8cL0='`
-->
<p id="hello">click me</p>
<script>
const hello = document.querySelector("#hello");
hello.addEventListener("click", () => {
console.log("Hello from an inline script");
});
</script>
Защита от кликджекинга
Директиву frame-ancestors можно использовать для управления тем, какие документы, если таковые имеются, могут встраивать этот документ во вложенный контекст просмотра, например в <iframe>. Это эффективная защита от атак типа кликджекинга, поскольку такие атаки предполагают встраивание целевого сайта на сайт, контролируемый злоумышленником.
Синтаксис frame-ancestors представляет собой подмножество синтаксиса директив загрузки: можно указать единственное значение-ключевое слово 'none' или одно либо несколько выражений источника. Однако доступны только выражения источника в виде схем, имён хостов или значения-ключевого слова 'self'.
Если ваш сайт не должен встраиваться в другие страницы, задайте для frame-ancestors значение 'none':
Content-Security-Policy: frame-ancestors 'none'
Эта директива представляет собой более гибкую замену заголовка X-Frame-Options.
Обновление небезопасных запросов
Веб-разработчикам настоятельно рекомендуется передавать всё содержимое по HTTPS. При переводе сайта на HTTPS иногда основной документ передаётся по HTTPS, а ресурсы — по HTTP, например с помощью такой разметки:
<script src="http://example.org/my-cat.js"></script>
Это называется смешанным содержимым, и наличие небезопасных ресурсов значительно ослабляет защиту, обеспечиваемую HTTPS. Согласно реализуемому браузерами алгоритму обработки смешанного содержимого, если документ передаётся по HTTPS, небезопасные ресурсы делятся на «обновляемое содержимое» и «блокируемое содержимое». Обновляемое содержимое переводится на HTTPS, а блокируемое — блокируется, что может нарушить работу страницы.
Окончательное решение проблемы смешанного содержимого — загрузка разработчиками всех ресурсов по HTTPS. Однако даже если сайт действительно может передавать всё содержимое по HTTPS, разработчику может быть очень сложно (а для архивного содержимого — практически невозможно) переписать все URL, используемые сайтом для загрузки ресурсов.
Директива upgrade-insecure-requests предназначена для решения этой проблемы. У этой директивы нет значения: чтобы задать её, достаточно указать имя директивы:
Content-Security-Policy: upgrade-insecure-requests
Если директива задана для документа, браузер автоматически заменит HTTP на HTTPS в URL в следующих случаях:
- при запросах на загрузку ресурсов (например, изображений, скриптов или шрифтов)
- при навигационных запросах (например, переходах по ссылкам), если их источник совпадает с источником документа
- при навигационных запросах во вложенных контекстах просмотра, например во фреймах iframe
- при отправке форм
Однако запросы верхнего уровня навигации, направленные на другой источник, обновляться не будут.
Например, предположим, что документ по адресу https://example.org передаётся с CSP, содержащей директиву upgrade-insecure-requests, и включает такую разметку:
<script src="http://example.org/my-cat.js"></script> <script src="http://not-example.org/another-cat.js"></script>
Браузер автоматически обновит оба этих запроса до HTTPS.
Предположим, что документ также содержит следующее:
<a href="http://example.org/more-cats">See some more cats!</a> <a href="http://not-example.org/even-more-cats">More cats, on another site!</a>
Браузер обновит первую ссылку до HTTPS, но не вторую, поскольку она ведёт на другой источник.
Эта директива не заменяет заголовок Strict-Transport-Security (также известный как HSTS), поскольку она не обновляет внешние ссылки на сайт. Сайтам следует использовать эту директиву вместе с заголовком Strict-Transport-Security.
Требование доверенных типов
Директивы require-trusted-types-for и trusted-types позволяют защититься от клиентских атак типа межсайтового скриптинга (XSS). Они гарантируют, что любые входные данные будут преобразованы для обеспечения безопасности, прежде чем передаваться API веб-платформы, который иначе мог бы выполнить их как код. Директивы require-trusted-types-for и trusted-types можно использовать для применения API Trusted Types. Это позволяет защититься от клиентских атак типа межсайтового скриптинга (XSS), требуя, чтобы любые входные данные проходили через функцию преобразования, которая позволяет сделать их безопасными до передачи API веб-платформы, способному иначе выполнить их как код.
Точки внедрения и санитизация
Некоторые API веб-платформы известны как точки внедрения. Это API, которым можно передать входные данные, обычно в виде строки, и которые могут интерпретировать эти данные как код. В этом руководстве мы уже рассмотрели eval(), но существует множество других точек внедрения, например Element.innerHTML или Document.write().
Если злоумышленник может передать вашему сайту специально сформированные входные данные, а сайт передаст их одной из таких точек внедрения, злоумышленник сможет выполнить вредоносный код.
Некоторые точки внедрения, например eval(), очень сложно использовать безопасно, и мы уже видели, что CSP обычно полностью блокирует их. Другие можно сделать безопаснее, если обработать передаваемые им данные, удалив небезопасные элементы. Эта практика называется санитизацией.
API доверенных типов
С помощью API доверенных типов в точки внедрения можно передавать доверенные типы вместо строк. Доверенные типы — это объекты, полученные в результате обработки потенциально опасных входных данных функцией преобразования. Обычно при таком преобразовании входные данные санитизируются: из них удаляются элементы, которые могут сделать их исполняемыми (например, теги <script>).
По умолчанию ваш код может передавать в точки внедрения как доверенные типы, так и несантитизированные строки. Однако если вы добавите в CSP директиву require-trusted-types-for со значением 'script', браузер разрешит вашему сайту передавать в точки внедрения только доверенные типы. Например, следующий код вызовет исключение:
const possiblyXSS = "<p>I might be XSS</p>";
const target = document.querySelector("#target");
target.innerHTML = possiblyXSS;
// Will throw an exception if `require-trusted-types-for` is set
Объекты доверенных типов создаются с помощью определяемого пользователем объекта политики. Ваш код может создать любой объект политики, в том числе такой, функция преобразования которого фактически не санитизирует входные данные и поэтому не защищает вас. Чтобы свести этот риск к минимуму, можно добавить директиву trusted-types. В ней перечисляются имена допустимых политик, и браузер разрешает использовать только указанные политики.
Тестирование политики
Для упрощения внедрения CSP можно использовать в режиме только отчётов. Политика не применяется, но обо всех нарушениях отправляются отчёты на указанный в политике конечный адрес для отчётов. Кроме того, заголовок режима только отчётов можно использовать для тестирования будущей версии политики, не внедряя её фактически.
Чтобы указать политику, можно использовать HTTP-заголовок Content-Security-Policy-Report-Only, например так:
Content-Security-Policy-Report-Only: policy
Если в одном ответе присутствуют заголовки Content-Security-Policy-Report-Only и Content-Security-Policy, применяются обе политики. Политика, указанная в заголовках Content-Security-Policy, применяется, а политика Content-Security-Policy-Report-Only формирует отчёты, но не применяется.
Обратите внимание: в отличие от обычной политики безопасности содержимого, политику режима только отчётов нельзя задать в элементе <meta>.
Отчёты о нарушениях
Рекомендуемый способ отправки отчётов о нарушениях CSP — использовать Reporting API, объявив конечные точки в Reporting-Endpoints и указав одну из них в качестве целевого адреса для отчётов CSP с помощью директивы report-to заголовка Content-Security-Policy.
Предупреждение: Для указания целевого URL-адреса отчётов о нарушениях CSP можно также использовать директиву report-uri CSP. При этом выполняется операция POST, в ходе которой отправляется JSON-отчёт немного иного формата с типом содержимого Content-Type — application/csp-report. Этот способ считается устаревшим, но следует указывать оба варианта, пока директива report-to не будет поддерживаться всеми браузерами. Подробнее об этом способе см. в разделе report-uri.
Сервер может сообщить клиентам, куда отправлять отчёты, используя HTTP-заголовок ответа Reporting-Endpoints. В этом заголовке указывается один или несколько URL-адресов конечных точек в виде списка, разделённого запятыми. Например, чтобы определить конечную точку для отчётов с именем csp-endpoint, принимающую отчёты по адресу https://example.com/csp-reports, заголовок ответа сервера может выглядеть так:
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"
Если вам нужны несколько конечных точек для обработки разных типов отчётов, укажите их следующим образом:
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports",
hpkp-endpoint="https://example.com/hpkp-reports"
Затем с помощью директивы report-to заголовка Content-Security-Policy можно указать, какую из заданных конечных точек использовать для отправки отчётов. Например, чтобы отправлять отчёты о нарушениях CSP для default-src на адрес https://example.com/csp-reports, можно отправить следующие заголовки ответа:
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports" Content-Security-Policy: default-src 'self'; report-to csp-endpoint
При возникновении нарушения CSP браузер отправляет отчёт в виде объекта JSON на указанный адрес с помощью HTTP-операции POST и указывает тип содержимого Content-Type — application/reports+json. Отчёт представляет собой сериализованную форму объекта CSPViolationReport со свойством type, значение которого равно "csp-violation".
Типичный объект может выглядеть так:
{
"age": 53531,
"body": {
"blockedURL": "inline",
"columnNumber": 39,
"disposition": "enforce",
"documentURL": "https://example.com/csp-report",
"effectiveDirective": "script-src-elem",
"lineNumber": 121,
"originalPolicy": "default-src 'self'; report-to csp-endpoint-name",
"referrer": "https://www.google.com/",
"sample": "console.log(\"lo\")",
"sourceFile": "https://example.com/csp-report",
"statusCode": 200
},
"type": "csp-violation",
"url": "https://example.com/csp-report",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"
}
Вам потребуется настроить сервер для приёма отчётов с указанным форматом JSON и типом содержимого. Сервер, обрабатывающий эти запросы, может хранить или обрабатывать поступающие отчёты наиболее подходящим для ваших задач способом.
См. также
© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP