近期,多位Web开发者和安全工程师在技术社区反映,Content Security Policy(CSP)中的“unsafe-hashes”指令在实际部署中出现行为异常,浏览器对同一内联脚本或事件处理器计算出的哈希值,与开发者预设的哈希值不一致,导致CSP规则无法按预期生效。这一问题已影响多个主流浏览器的稳定版本,引发业界对CSP标准实现一致性的担忧。

背景:CSP与“unsafe-hashes”的作用

Content Security Policy(内容安全策略)是Web安全领域的一项核心机制,用于防范跨站脚本攻击(XSS)。通过CSP头部,网站可以指定允许加载的脚本、样式、图片等资源的来源,从而限制攻击者注入恶意代码。其中,“unsafe-hashes”是CSP Level 2引入的一个指令,旨在允许开发者通过SHA-256、SHA-384或SHA-512哈希值,精确授权特定的内联脚本或事件处理器(如onclickonerror等),而无需完全开启“unsafe-inline”。这对于那些必须使用少量内联代码的旧系统或第三方库尤为重要。

例如,开发者可以在CSP头中写入类似script-src 'unsafe-hashes' 'sha256-abc123...'的规则,期望浏览器在执行该哈希对应的内联脚本时放行。然而,近期报告显示,浏览器在计算哈希时采取了与开发者预期不同的算法,使得规则形同虚设。

问题核心:浏览器期望的哈希与开发者提供的不同

根据多个测试案例,当开发者使用标准工具(如openssl或在线哈希计算器)对一段内联脚本进行哈希计算后,将结果填入CSP头部,Chrome、Firefox等浏览器在加载页面时,控制台仍会报出违反CSP规则的错误,并提示“拒绝执行内联脚本,因为它违反了以下内容安全策略指令”,同时会显示浏览器自己计算出的期望哈希值。对比发现,两者往往仅因编码格式、空白字符、转义序列或MIME类型的不同而出现差异。

常见差异场景包括:

  • 空白字符处理:开发者计算的哈希包含源代码中的换行、缩进,但浏览器在提取内联脚本时可能自动规范化空白,或者保留原始DOM中的内容(如<script>标签内的前后空白)。
  • 转义与实体:HTML实体(如&amp;)在浏览器解析后转化为&,导致原始字符串与哈希计算时的字符串不一致。
  • 引号与编码:内联事件处理器(如onclick="alert(1)")在属性值中的引号、双引号处理方式因浏览器不同而有所差异;某些浏览器会保留属性中的引号,而另一些可能会剥离或转义。
  • MIME类型干扰:对于内联脚本,CSP规范要求根据HTML中实际出现的文本计算哈希,但部分浏览器会考虑父文档的编码声明(如UTF-8 vs. ISO-8859-1),从而改变字节序列。

实际测试中,一个简单的<script>console.log(1)</script>,在Chrome 120中成功匹配sha256-...需要的哈希,但在Firefox 121中却提示“期望的哈希是sha256-xxx”,而开发者填入的与浏览器期望的完全不同。跨浏览器的不一致性使得开发者无法一次性生成万能的哈希值。

影响与困扰:安全性与可用性的两难

“unsafe-hashes”的设计初衷是在安全性和灵活性之间取得平衡,但当前的问题直接削弱了其可用性。对于依赖CSP保护的中大型网站,如果哈希规则无法生效,要么降级为更宽松的“unsafe-inline”(增加XSS风险),要么耗费大量时间对每个内联代码块进行浏览器兼容性测试和单独调整。更糟糕的是,当哈希错误发生后,浏览器通常会直接拒绝执行内联代码,导致页面功能异常(如按钮点击无响应、表单无法提交),用户交互受阻。

多位安全专家指出,该问题可能源于CSP规范中对哈希计算输入源的描述不够精确。虽然W3C标准规定应对“脚本的源文本”进行哈希,但在实际实现中,浏览器对“源文本”的理解存在分歧——是未解析的原始HTML字符串,还是经过DOM解析后的内容?不同引擎的解析差异被放大。

行业反应与临时方案

Mozilla和Google的开发团队已注意到相关反馈,但尚未就规范修订达成明确共识。目前,开发者社区总结出以下临时缓解措施:

  1. 严格格式化内联代码:编写内联脚本时,保证无额外空白、无换行,且使用一致的引号风格。
  2. 避免事件属性:尽量使用addEventListener替代内联事件处理器,从而使用更稳定的'nonce-value''strict-dynamic'机制。
  3. 采用多浏览器测试:将生成的哈希同时在Chrome、Firefox、Safari中验证,记录每个浏览器的“期望哈希”,在CSP头部中列出多个哈希值。
  4. 工具升级:使用CSP评估工具(如第三方Chrome扩展)时,确保其与目标浏览器采用相同的哈希生成逻辑。

展望:标准化之路仍需推进

此事件再次提醒业界,即使是最细致的安全规范,在跨浏览器实现时也可能出现裂缝。CSP“unsafe-hashes”作为对内联代码的精细控制手段,其价值毋庸置疑,但前提是所有浏览器达成一致的哈希计算规则。W3C Web Application Security工作组需要加快对规范中哈希输入定义的澄清,并推动浏览器厂商同步修改。对于广大开发者而言,在正式修复前,建议暂时优先采用nonce或strict-dynamic策略,将内联代码数量降到最低,以规避哈希不一致带来的安全与功能风险。

(完)