CSP 中的 nonce 与 hash 严格模式脚本执行
请解释 CSP 中 nonce(随机数)和 hash(哈希)两种严格脚本执行策略的工作原理,以及如何在后端生成 nonce 并与前端模板配合。
回答
苦行僧
问题背景:'unsafe-inline' 允许所有内联脚本执行,不安全。strict-dynamic 模式配合 nonce/hash 可实现精确控制。
Nonce(单次随机数)策略:
# 响应头
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa';
<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
console.log('这个脚本会执行');
</script>
<script>
console.log('缺少 nonce,不会执行');
</script>
服务器端实现:
// Express 示例
app.use((req, res, next) => {
// 生成随机 nonce
const nonce = crypto.randomBytes(16).toString('base64');
res.locals.cspNonce = nonce;
res.setHeader(
'Content-Security-Policy',
`default-src 'self'; script-src 'nonce-${nonce}' 'strict-dynamic'`
);
next();
});
// 模板中(EJS/Pug/React Helmet)
// <script nonce="<%= cspNonce %>">...</script>
Hash 策略:
# 计算内联脚本的 SHA 哈希并添加到策略
Content-Security-Policy: script-src 'sha256-abc123...'
// 生成 hash
const crypto = require('crypto');
const script = 'console.log("fixed")';
const hash = 'sha256-' + crypto.createHash('sha256').update(script).digest('base64');
- 适合不频繁变动的内联脚本
- 脚本内容变化后需要更新 hash
strict-dynamic 机制:
script-src 'nonce-abc123' 'strict-dynamic'
- 允许 nonce 信任的脚本通过
document.createElement('script')创建的新脚本执行 - 无需为动态生成的脚本生成 nonce
strict-dynamic会覆盖'self'和'unsafe-inline'(在支持的浏览器中)
推荐实践:使用 nonce + strict-dynamic + fallback 白名单处理旧浏览器。