首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Content Security Policy:nonce 和 strict-dynamic 到底怎么配合

Content Security Policy:nonce 和 strict-dynamic 到底怎么配合

原创
作者头像
跨境出海观察员
发布于 2026-11-12 09:26:10
发布于 2026-11-12 09:26:10
320
举报

“加了 CSP 还是被 XSS 打穿”是前端安全里常见的困惑。很多站点只写了 default-src 'self',却在页面里大量使用内联脚本;为了让它跑起来,又加回 'unsafe-inline',CSP 就只剩下一个响应头。

一、CSP 管什么

Content Security Policy 通过响应头告诉浏览器:哪些来源的脚本、样式、图片、字体、连接可以加载,哪些内联代码可以执行。它能显著降低 XSS 的影响,但不是 XSS 过滤器;服务端输出转义仍然要做。

举个常见例子:服务端把用户输入直接拼进 HTML,CSP 不会阻止这段 HTML 被解析;它只能在脚本执行时决定是否放行。输出编码和参数化查询仍然是第一道防线。

一个常见的严格配置:

代码语言:http
复制
Content-Security-Policy: default-src 'self'; script-src 'nonce-abc123' 'strict-dynamic'; object-src 'none'; base-uri 'none'

default-src 'self' 限制默认来源;script-src 定义脚本来源;object-src 'none' 禁用插件;base-uri 'none' 防止 <base> 标签被注入后改写相对路径。script-src 还可以拆成 script-src-elem(外部脚本和 <script> 标签)和 script-src-attr(内联事件处理器),便于分别收紧。

frame-ancestors 控制哪些页面可以嵌入当前页面,能降低点击劫持风险;form-action 限制表单提交目标,防止注入的表单把数据发到外部。

二、nonce 怎么工作

nonce 是每次响应生成的随机值,服务端把它写进 CSP 头和页面里的 <script nonce="...">。两者一致,浏览器才执行这段内联脚本。

代码语言:html
复制
<script nonce="abc123">
  console.log('allowed');
</script>

关键点是“每次响应都不同”。如果 nonce 被缓存、被写死在静态 HTML 里,攻击者就能预测或复用,nonce 就失去意义。使用 CDN 或页面缓存时,要确保 CSP 头和 HTML 一起生成,不能缓存一份带 nonce 的页面发给所有人。

如果页面是静态生成的,无法为每个响应生成 nonce,可以用 hash:script-src 'sha256-...'。hash 适合固定内容的内联脚本,但脚本一改,hash 就要更新。

三、strict-dynamic 解决什么

传统 CSP 用域名白名单,但现代前端往往从 CDN 动态加载脚本,白名单很难维护。'strict-dynamic' 的规则是:只要脚本由已受信任的脚本创建,就允许执行;主机白名单在支持 CSP3 的浏览器里会被忽略。

它适合配合 nonce 使用:入口脚本带 nonce,后续它加载的脚本自动获得信任。但这也意味着,一旦入口脚本被攻破,它加载的恶意脚本同样会被信任;strict-dynamic 不是“更安全”,而是把信任边界集中到了入口脚本。strict-dynamic 还需要配合 'nonce-...' 或 hash 使用;只写 'strict-dynamic' 没有信任根,等于没有放行任何脚本。

四、常见坑

第一,nonce 和 'unsafe-inline' 同时存在。支持 CSP2 的浏览器会忽略 'unsafe-inline',但老浏览器可能仍然执行内联脚本;不要指望它做降级。

第二,内联事件处理器。onclick="..." 不受 nonce 保护,需要改成 addEventListener,或者用 'unsafe-hashes' 加 hash 放行,但后者会扩大攻击面。

第三,第三方脚本。分析、客服、广告脚本往往需要放宽 script-src;能放到独立 iframe 或单独页面的,尽量不要和主站共用策略。

第四,先用 Content-Security-Policy-Report-Only 观察。直接上强制策略容易把业务打挂;先收集报告,确认没有漏配,再切换。report-uri 和 report-to 可以收集违规报告;报告里可能带页面 URL 和拦截指令,接入日志系统时要注意脱敏。

报告阶段不要只看数量,要看被拦截的资源是不是业务必需;有些第三方脚本会动态插入内联代码,必须先改造成 nonce/hash 才能收紧。

五、上线顺序

  1. 先上 Content-Security-Policy-Report-Only,收集被拦截的资源和页面;
  2. 处理内联脚本和 onclick 这类事件处理器;
  3. 用 nonce 或 hash 放行入口脚本;
  4. 再收紧 default-src、object-src、base-uri、frame-ancestors;
  5. 最后切换强制模式,保留报告端点观察一段时间。

六、总结

CSP 的核心不是“写一个头”,而是把脚本来源收窄到可解释的范围。nonce 负责内联脚本,strict-dynamic 负责动态加载,report-only 负责灰度。三者配合,才能在不影响业务的前提下降低 XSS 风险。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、CSP 管什么
  • 二、nonce 怎么工作
  • 三、strict-dynamic 解决什么
  • 四、常见坑
  • 五、上线顺序
  • 六、总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档