Featured image of post 为什么以及如何在 CSP 中采用“严格动态”Featured image of post 为什么以及如何在 CSP 中采用“严格动态”

为什么以及如何在 CSP 中采用“严格动态”

了解内容安全策略 (CSP) 3 级中“严格动态”指令的优势,以信任动态注入的脚本标记。

介绍

实施内容安全策略 (CSP) 是减轻跨站脚本 (XSS) 风险的高效方法。然而,配置和维护策略可能会成为一个令人头疼的问题。

对于使用第三方 SDK(例如 Google 跟踪代码管理器、分析信标、支付小部件或社交共享按钮)的网站,这些脚本通常会从嵌套域动态加载(注入)其他脚本。这迫使开发人员在 script-src 指令中维护一个长而脆弱的外部域白名单。

CSP Level 3 中引入的 'strict-dynamic' 指令解决了这个问题。本文解释了它的工作原理以及如何应用它。


1. CSP中域名白名单的局限性

在旧版本的 CSP(级别 1 和级别 2)中,开发人员依赖于将允许加载和执行脚本的特定域列入白名单:

Content-Security-Policy: script-src 'self' https://www.google-analytics.com https://cdn.jsdelivr.net;

白名单的挑战

  1. 维护费用: 如果第三方 SDK(例如 google-analytics.com)更新其内部逻辑以从新域(例如 static.cloudflareinsights.com)获取帮助程序脚本,您的网站将阻止新脚本。您必须立即更新 CSP 标头才能恢复功能。
  2. 白名单绕过: 如果列入白名单的 CDN(例如 cdnjs.cloudflare.com)托管具有已知漏洞的过时库或允许任意执行的脚本,攻击者可以使用它绕过您的 CSP 并执行恶意代码。

2.“严格动态”如何运作

'strict-dynamic' 指令通过建立信任链来简化脚本信任管理。

核心规则

  • 信任链传播: 启用 'strict-dynamic' 后,任何已受信任的脚本(通过加密的 noncehash)都被允许动态加载新的脚本标签。浏览器自动信任受信任脚本的子代。
  • 域白名单覆盖: 当浏览器在 CSP 中检测到 'strict-dynamic' 时,它忽略 script-src 指令中的任何基于域的源表达式(如 https://example.com)和 'self' 。信任严格由加密随机数或哈希值决定。

3. 实现示例

要使用 'strict-dynamic',您必须将其与加密 nonce (根据请求生成的随机令牌)或文件 hash 配对。

HTTP 标头配置

Content-Security-Policy: script-src 'nonce-RndOmValue123' 'strict-dynamic' 'unsafe-inline' https:;
  • 'nonce-RndOmValue123':信任包含此匹配随机数属性的任何内联或外部脚本标记。
  • 'strict-dynamic':指示浏览器信任由随机数可信脚本动态注入的任何子脚本。
  • 'unsafe-inline' https::不支持 CSP 3 级的旧版浏览器的后备选项。当 'strict-dynamic' 存在时,现代浏览器会自动忽略这些选项。

HTML 标记示例

<!-- The browser executes this script because of the matching nonce attribute -->
<script nonce="RndOmValue123">
  // Since this parent script is trusted, strict-dynamic allows it to inject child scripts
  const script = document.createElement('script');
  script.src = 'https://example.com/dynamic-library.js';
  document.head.appendChild(script);
</script>

通过此设置,您无需将 https://example.com 添加到 CSP 标头。浏览器执行 dynamic-library.js 因为它是由受信任的父脚本注入的。


4. 关键考虑因素:解析器注入的脚本

请注意,某些遗留库使用 document.write('<script src="..."></script>') 注入脚本。这些解析器注入的脚本不受 'strict-dynamic' 信任,并将被浏览器阻止。

要解决此问题,您必须更新脚本加载代码以使用标准的现代 DOM API,例如 document.createElement('script')

结论

在 CSP 中采用 'strict-dynamic' 指令可提高安全性,同时简化维护。

  1. 无需维护较长的第三方域白名单。
  2. 允许受信任的父脚本动态注入依赖项。
  3. 包含后备表达式 ('unsafe-inline' https:) 以支持旧版浏览器。

考虑在项目的 CSP 配置中使用 'strict-dynamic' 和加密随机数来保护您的应用程序。