Как использовать сгенерированный nonce
Пресет Nonce для CSP уже выбран: 16 случайных байтов в Base64, то есть 24-символьное значение, которое заканчивается на
==.В блоке Готово к вставке появятся заголовок и подходящий тег, например:
Content-Security-Policy: script-src 'nonce-SQu4/GjzG3plY6CUwsx8gA==' <script nonce="SQu4/GjzG3plY6CUwsx8gA==" src="/app.js"></script>Используйте его, чтобы опробовать политику на локальной тестовой странице или в конфиге стейджинга. Для другого значения нажмите Сгенерировать заново.
Nonce, скопированный с этой страницы, одинаков в каждом запросе, так что в продакшене он ни от чего не защищает. В реальных развёртываниях его генерирует серверный код, как показано ниже.
Как работает политика на nonce
С script-src 'nonce-…' браузер запускает скрипт, только если его атрибут nonce совпадает со значением в заголовке именно этого ответа. Ваши собственные теги <script> получают атрибут из шаблонов. Разметка, внедрённая атакующим через XSS, значения не знает, потому что оно меняется при каждой загрузке страницы, и браузер отказывается её выполнять.
На практике важны три детали:
- Значение сравнивается как текст. Копируйте его точно, вместе с
=, и оставляйте одинарные кавычки вокруг'nonce-…'в заголовке, но не в атрибуте. - Браузеры прячут атрибут. После загрузки страницы
getAttribute("nonce")возвращает пустую строку, а CSS-селекторы значения не видят; это закрывает некоторые трюки с утечкой. Скриптам, которым оно нужно, следует читать свойствоnonce. 'strict-dynamic'позволяет доверенному скрипту с nonce подгружать другие скрипты, не выдавая каждому свой nonce. В современной политике это обычный спутник nonce, и с ним браузер игнорирует белые списки хостов вродеhttps:.
Политику можно передать и в теге <meta http-equiv="Content-Security-Policy">; nonce там тоже работают, хотя некоторые директивы, например frame-ancestors, в meta-теге не действуют.
Генерация nonce для каждого ответа
Сервер создаёт 16 случайных байтов на каждый запрос, кладёт их в заголовок и передаёт то же значение в шаблон. В Node.js без всяких фреймворков:
const crypto = require("crypto");
const http = require("http");
http.createServer((req, res) => {
const nonce = crypto.randomBytes(16).toString("base64");
res.setHeader("Content-Security-Policy",
`script-src 'nonce-${nonce}' 'strict-dynamic'; object-src 'none'; base-uri 'none'`);
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.end(`<script nonce="${nonce}" src="/app.js"></script>`);
}).listen(8080);
В Python base64.b64encode(secrets.token_bytes(16)).decode() даёт такое же значение; фреймворки с CSP-middleware обычно отдают его в шаблон переменной. Что бы вы ни использовали, убедитесь, что кеш перед сервером не сохраняет HTML с одним nonce и не раздаёт его всем посетителям. Если кешируется тело страницы, а заголовок меняется, ломаются все скрипты; если кешируется и то и другое, nonce повторяется для всех.
Nonce, хеши и SRI
Nonce одобряет тег скрипта для одного ответа. Хеш в CSP одобряет конкретный встроенный скрипт навсегда: 'sha256-…' содержит SHA-256 в Base64 от точного текста между <script> и </script>. Для <script>alert('Hello');</script> это:
script-src 'sha256-gKHd+pSZOJ3MwBsFalomyNobAcinjJ44ArqbIKlcniQ='
Хеши подходят статическим сайтам и встроенным фрагментам, которые никогда не меняются; считайте их инструментом SHA-256 в Base64 и следите за пробелами: один лишний пробел меняет хеш. Subresource Integrity добавляет третий механизм с теми же дайджестами в Base64: integrity="sha384-…" на <script src> заставляет браузер проверить сам скачанный файл. Такие теги собирает генератор SRI-хешей.
Вопросы и ответы
Какой длины должен быть nonce для CSP?
Спецификация CSP требует не меньше 128 бит случайности до кодирования, то есть 16 байтов или 24 символа Base64. Длиннее можно, а с более коротким защита слабеет.
Можно использовать один и тот же nonce на всех страницах?
Нет. Постоянный nonce виден в вашем HTML, так что атакующий, который может внедрить разметку, скопирует его в свой тег <script>. Генерируйте новый для каждого HTTP-ответа.
Может ли статический сайт использовать nonce в CSP?
Только если перед ним стоит что-то, что переписывает каждый ответ, например edge-функция, которая вставляет свежий nonce. Чисто статический хостинг отдаёт всем одни и те же байты, поэтому используйте хеши скриптов ('sha256-…') или внешние файлы из 'self'.
Можно ли в nonce использовать URL-safe символы?
Да. Грамматика CSP принимает оба алфавита Base64: буквы, цифры, +, /, - и _, и до двух = в конце. Чаще всего выбирают стандартный Base64.