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

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

<ph type="x-smartling-placeholder">
</ph> 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 或哈希值来避免绕过许可名单

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

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

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

CSP:

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 作为不合规浏览器的回退方案。如果浏览器支持 Nonce/hash,unsafe-inline 将会被忽略。

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

如何制定严格的 CSP

以下是将严格 CSP 与基于 Nonce 的政策结合使用的示例。

CSP:

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。