内容安全策略 (CSP) 的实现
Content-Security-Policy HTTP 标头提供了对网站可加载代码及其允许执行的操作的精细控制。
问题
本文主要关注的焦点是跨站脚本 (XSS) 攻击。这些攻击通常是由于缺乏对网站资源加载来源的控制和意识所致。随着网站变得越来越庞大和复杂,并且越来越依赖 JavaScript 库等第三方资源,这一问题也变得更难管理。
CSP 还可以帮助修复其他问题,这些问题将在其他文章中介绍
解决方案
实现 严格 CSP 是利用 CSP 缓解 XSS 漏洞的最佳方法。它使用基于 nonce 或 hash 的获取指令,以确保只有包含正确 nonce 或 hash 的脚本和/或样式才能执行。黑客插入的 JavaScript 将无法运行。
严格 CSP 还
- 禁用不安全的 内联 JavaScript 的使用,这意味着内联 事件处理程序属性(如
onclick)的用法。这可以防止不正确转义的用户输入被 Web 浏览器解释为 JavaScript。 - 禁用 有风险的 API 调用(如
eval())的使用,这是script-src指令的另一个效果。 - 通过
object-src 'none'禁用所有对象嵌入。 - 通过
base-uri 'none';禁用<base>元素的用法来设置基本 URI。
与基于位置的策略(也称为允许列表策略,即您指定脚本可以从哪些域运行)相比,严格 CSP 更受青睐。这是因为允许列表策略最终经常允许不安全的域,这违背了拥有 CSP 的初衷,而且它们可能变得非常庞大和难以管理,特别是如果您试图允许需要大量第三方脚本才能正常运行的服务。
实施 CSP 的步骤
实施严格 CSP,然后开始精确定位因该策略而加载失败的资源,并采取措施来解决这些问题。
注意: 在使用 Content-Security-Policy 标头实施任何实际的 CSP 之前,建议您先使用 Content-Security-Policy-Report-Only HTTP 标头进行测试;请参阅下方的 报告模式 CSP。
- 决定是使用 nonces 还是 hashes。如果您可以动态生成内容,则应使用 nonces;如果您需要提供静态内容,则应使用 hashes。
- 按照 解决方案 部分的概述实施严格 CSP。确保您想要运行的外部和内部脚本(通过
<script>元素包含)在服务器上已将其正确的 nonce 插入到nonce属性中。如果您改用 hashes,则外部脚本应将正确的 hash 插入到integrity属性中。 - 如果允许的脚本继续加载第三方脚本,这些脚本将无法加载,因为它们没有所需的 nonce 或 hash。通过添加
strict-dynamic指令来解决此问题,该指令使第一个脚本加载的脚本具有相同的信任级别,而无需显式提供 nonce 或 hash。 - 重构严格 CSP 所不允许的模式,例如内联事件处理程序和
eval()。例如,将内联事件处理程序替换为脚本内的addEventListener()调用。 - 除非网站需要嵌入能力,否则应使用
object-src 'none'禁用其执行。 - 如果您无法删除
eval()的用法,可以将unsafe-eval关键字添加到您的严格 CSP 中以允许它们,尽管这会大大削弱 CSP 的安全性。 - 如果您无法删除事件处理程序属性,可以将
unsafe-hashes关键字添加到您的严格 CSP 中以允许它们。这在一定程度上不安全,但比允许所有内联 JavaScript 安全得多。
如果您无法使严格 CSP 生效,基于允许列表的 CSP 比没有 CSP 要好得多,像 default-src https: 这样的 CSP 仍然提供了一些保护,禁用了不安全的内联/eval(),并且只允许通过 HTTPS 加载资源(图像、字体、脚本等)。
警告: 如果可能,请避免在 CSP 中包含不安全的来源。例如:
unsafe-inline.script-src、object-src或default-src中的data:URI。- 过于宽泛的来源或表单提交目标。
如果您无法使用 Content-Security-Policy 标头,页面可以改用 <meta http-equiv="Content-Security-Policy" content="…"> 元素。这应该是文档 <head> 中的第一个 <meta> 元素。
报告模式 CSP
在使用 Content-Security-Policy 标头实施任何实际的 CSP 之前,建议您先使用 Content-Security-Policy-Report-Only HTTP 标头进行测试。这使您能够查看使用该策略是否会发生任何违规行为。
网站应使用 report-to 和 report-uri 报告指令。这些指令会导致浏览器将关于 CSP 违规的 JSON 报告 POST 到端点(在 report-to 的情况下,在 Reporting-Endpoints 标头中指定)。这使得 CSP 违规能够被快速捕获和修复。
注意: report-to 指令比已弃用的 report-uri 指令更受推荐。但是,两者仍然是必需的,因为 report-to 尚未获得完整的跨浏览器支持。