确保 CSP 能够有效防范 XSS 攻击

内容安全政策 (CSP) 有助于确保网站所有者信任网页中加载的所有内容。CSP 可以阻止攻击者注入的不安全脚本,从而缓解跨站脚本攻击 (XSS) 攻击。不过,如果 CSP 不够严格,则可以轻松绕过 CSP。如需了解详情,请参阅通过严格的内容安全政策 (CSP) 缓解跨站脚本攻击 (XSS)。Lighthouse 会收集在主文档中强制执行的 CSP,并报告 CSP Evaluator 中无法绕过的问题。

Lighthouse 报告警告在强制执行模式下未找到 CSP。
Lighthouse 报告警告:在强制执行模式下找不到 CSP。

不可绕过的 CSP 的必要做法

请遵循以下做法,确保无法绕过您的 CSP。如果可以绕过 CSP,Lighthouse 将发出一个严重级别为“高”的警告。

CSP 目标 XSS

要以 XSS 为目标,CSP 应包含 script-srcobject-srcbase-uri 指令。CSP 还应不存在语法错误。

script-srcobject-src 分别用于保护网页免受不安全脚本和不安全插件的影响。或者,您也可以使用 default-src 配置宽泛的政策,以代替 script-srcobject-src许多指令

base-uri 可防止注入未经授权的 <base> 标记,此类标记可用于将所有相对网址(例如脚本)重定向到攻击者控制的网域。

CSP 使用 Nonce 或哈希来避免绕过许可名单

script-src 配置许可名单的 CSP 依赖于以下假设:来自受信任网域的所有响应都是安全的,可以作为脚本执行。但是,这一假设不适用于现代应用;一些常见的中性模式(例如公开 JSONP 接口托管 AngularJS 库的副本)允许攻击者逃脱 CSP 的限制。

在实践中,虽然对应用开发者而言,这并不明显,但大多数的 script-src 许可名单都可以被存在 XSS bug 的攻击者规避,并且几乎无法防范脚本注入。相比之下,基于 Nonce 和基于哈希的方法不会遇到这些问题,并且更容易采用和维护更安全的政策。

例如,以下代码使用托管在受信任网域中的 JSONP 端点来注入由攻击者控制的脚本:

内容安全政策:

script-src https://trusted.example.com

HTML:

<script src="https://trusted.example.com/path/jsonp?callback=alert(document.domain)//"></script>

为避免被绕过,CSP 应允许分别使用 Nonce 或哈希值的脚本,并使用“Strict-dynamic”而不是许可名单。

有关安全 CSP 的其他建议

实施以下做法以提高安全性和兼容性。如果 CSP 未遵循其中任何一项建议,则 Lighthouse 将发出中等严重级别的警告。

配置 CSP 报告

配置报告目标位置有助于监控任何损坏情况。您可以使用 report-urireport-to 指令设置报告目标位置。目前,并非所有新型浏览器都支持 report-to,因此建议您同时使用二者,或仅使用 report-uri

如果有任何内容违反了 CSP,浏览器会向配置的目标发送报告。请确保您在此目标位置配置了用于处理这些报告的应用。

在 HTTP 标头中定义 CSP

CSP 可在元标记中定义如下:

<meta http-equiv="Content-Security-Policy" content="script-src 'none'">

不过,如果可以,您应该在 HTTP 响应标头中定义 CSP。在元标记之前的注入会绕过 CSP。此外,元标记 CSP 不支持 frame-ancestorssandbox 和报告。

确保 CSP 向后兼容

并非所有浏览器都支持 CSP nonces/hashes,因此建议您添加 unsafe-inline 作为不合规浏览器的后备方案。如果浏览器支持 nonces/hashes,则 unsafe-inline 会被忽略。

同样,并非所有浏览器都支持 strict-dynamic。建议将许可名单设为所有不合规浏览器的后备选项。在支持 strict-dynamic 的浏览器中,许可名单会被忽略。

如何开发严格的 CSP

以下示例展示了如何将严格 CSP 与基于 Nonce 的政策搭配使用。

内容安全政策:

script-src 'nonce-random123' 'strict-dynamic' 'unsafe-inline' https:;
object-src 'none';
base-uri 'none';
report-uri https://reporting.example.com;

HTML:

<script nonce="random123" src="https://trusted.example.com/trusted_script.js"></script>

random123 是每次网页加载时在服务器端生成的任何 base64 字符串。由于 Nonce 和 strict-dynamic,在现代浏览器中,unsafe-inlinehttps: 会被忽略。如需详细了解如何采用严格的 CSP,请参阅严格 CSP 指南

您可以使用 Lighthouse 和 CSP Evaluator 检查 CSP 是否存在可能的绕过情况。如果您想测试新的 CSP,而不想破坏现有页面,则可使用 Content-Security-Policy-Report-Only 作为标头名称,在“仅限报告”模式下定义 CSP。这会将 CSP 违规情况发送到您使用 report-toreport-uri 配置的所有报告目标,但实际上不会强制执行 CSP。