如何使用生成的 nonce
CSP nonce 预设已启用: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属性(property)。 'strict-dynamic'允许受信任的、带 nonce 的脚本继续加载其他脚本,而不需要每个脚本都有自己的 nonce。在现代策略中它通常与 nonce 搭配使用;启用后,浏览器会忽略https:这类主机白名单。
策略也可以通过 <meta http-equiv="Content-Security-Policy"> 标签下发;nonce 在那里同样有效,不过 frame-ancestors 等部分指令无效。
为每个响应生成 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 中间件的框架通常会把它作为模板变量提供。无论用什么方式,都要确保服务器前面的缓存不会把带有某个 nonce 的 HTML 存下来再发给每个访问者。只缓存页面内容而响应头在变,所有脚本都会失效;两者都缓存,则所有人拿到的都是同一个 nonce。
nonce、哈希与 SRI
nonce 只为一次响应批准脚本标签。CSP 哈希则永久批准某段特定的内联脚本:'sha256-…' 是 <script> 和 </script> 之间确切文本的 SHA-256,以 Base64 表示。对于 <script>alert('Hello');</script>,它是:
script-src 'sha256-gKHd+pSZOJ3MwBsFalomyNobAcinjJ44ArqbIKlcniQ='
哈希适用于静态网站和永不改变的内联代码片段;可以用 SHA-256 转 Base64 工具计算,注意空白字符,多一个空格哈希就会改变。子资源完整性(SRI)是第三种机制,使用同样的 Base64 摘要:在 <script src> 上加 integrity="sha384-…",浏览器就会校验下载的文件本身。SRI 哈希生成器 可以生成这类标签。
常见问题
CSP nonce 应该多长?
CSP 规范要求编码前至少有 128 位随机性,即 16 个字节或 24 个 Base64 字符。更长没问题,更短则会削弱保护。
能在每个页面上重复使用同一个 nonce 吗?
不能。固定的 nonce 在你的 HTML 中是可见的,能注入标记的攻击者可以把它复制到自己的 <script> 标签里。请为每个 HTTP 响应生成新的 nonce。
静态网站能使用 CSP nonce 吗?
只有在前面有组件改写每个响应时才行,例如插入新 nonce 的边缘函数。纯静态托管向所有人提供相同的字节,因此请改用脚本哈希('sha256-…')或来自 'self' 的外部文件。
nonce 中可以使用 URL 安全字符吗?
可以。CSP 语法接受两种 Base64 字母表:字母、数字、+、/、- 和 _,末尾最多两个 =。通常选用标准 Base64。