内容安全策略 (CSP) 的实现

Content-Security-Policy HTTP 标头提供了对网站可加载代码及其允许执行的操作的精细控制。

问题

本文主要关注的焦点是跨站脚本 (XSS) 攻击。这些攻击通常是由于缺乏对网站资源加载来源的控制和意识所致。随着网站变得越来越庞大和复杂,并且越来越依赖 JavaScript 库等第三方资源,这一问题也变得更难管理。

注意: CSP 是保护免受 XSS 攻击的完整策略的一部分。还有其他因素也同样重要,例如 输出编码清理

CSP 还可以帮助修复其他问题,这些问题将在其他文章中介绍

  • 通过阻止您的网站嵌入到 <iframe> 元素中来 防止点击劫持。这可以通过 CSP 的 frame-ancestors 指令来实现。
  • 通过将任何 HTTP 连接升级到 HTTPS 来 防止中间人 (MiTM) 攻击。CSP 的 upgrade-insecure-requests 指令对此有所帮助。请参阅 升级不安全请求

解决方案

实现 严格 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

  1. 决定是使用 nonces 还是 hashes。如果您可以动态生成内容,则应使用 nonces;如果您需要提供静态内容,则应使用 hashes。
  2. 按照 解决方案 部分的概述实施严格 CSP。确保您想要运行的外部和内部脚本(通过 <script> 元素包含)在服务器上已将其正确的 nonce 插入到 nonce 属性中。如果您改用 hashes,则外部脚本应将正确的 hash 插入到 integrity 属性中。
  3. 如果允许的脚本继续加载第三方脚本,这些脚本将无法加载,因为它们没有所需的 nonce 或 hash。通过添加 strict-dynamic 指令来解决此问题,该指令使第一个脚本加载的脚本具有相同的信任级别,而无需显式提供 nonce 或 hash。
  4. 重构严格 CSP 所不允许的模式,例如内联事件处理程序和 eval()。例如,将内联事件处理程序替换为脚本内的 addEventListener() 调用。
  5. 除非网站需要嵌入能力,否则应使用 object-src 'none' 禁用其执行。
  6. 如果您无法删除 eval() 的用法,可以将 unsafe-eval 关键字添加到您的严格 CSP 中以允许它们,尽管这会大大削弱 CSP 的安全性。
  7. 如果您无法删除事件处理程序属性,可以将 unsafe-hashes 关键字添加到您的严格 CSP 中以允许它们。这在一定程度上不安全,但比允许所有内联 JavaScript 安全得多。

如果您无法使严格 CSP 生效,基于允许列表的 CSP 比没有 CSP 要好得多,像 default-src https: 这样的 CSP 仍然提供了一些保护,禁用了不安全的内联/eval(),并且只允许通过 HTTPS 加载资源(图像、字体、脚本等)。

警告: 如果可能,请避免在 CSP 中包含不安全的来源。例如:

  • unsafe-inline.
  • script-srcobject-srcdefault-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-toreport-uri 报告指令。这些指令会导致浏览器将关于 CSP 违规的 JSON 报告 POST 到端点(在 report-to 的情况下,在 Reporting-Endpoints 标头中指定)。这使得 CSP 违规能够被快速捕获和修复。

注意: report-to 指令比已弃用的 report-uri 指令更受推荐。但是,两者仍然是必需的,因为 report-to 尚未获得完整的跨浏览器支持。

另见