内容安全策略 (CSP) 实现

The Content-Security-Policy HTTP header provides fine-grained control over the locations from which resources on a site can be loaded.

问题

本文档主要关注跨站脚本 (XSS) 攻击。这些攻击通常是由于缺乏对网站资源加载来源的控制和了解造成的。随着网站变得越来越大、越来越复杂,并越来越多地依赖于第三方资源(如 JavaScript 库),这个问题变得更加难以管理。

CSP 还可以帮助解决其他问题,这些问题在其他文章中有所介绍。

解决方案

实施强大的 CSP 是防止 XSS 漏洞的最佳方法。

CSP 的主要好处是它能够禁用不安全的内联 JavaScript。内联 JavaScript(无论是反射的还是存储的)都允许不正确转义的用户输入生成代码,这些代码会被 Web 浏览器解释为 JavaScript。通过使用 CSP 禁用内联 JavaScript,您可以消除针对您网站的大多数 XSS 攻击。

禁用内联 JavaScript 意味着所有 JavaScript 必须通过 <script> 元素的 src 属性从外部文件加载。内联 事件处理程序属性(如 onclick)以及直接插入到 <script> 标签中的 JavaScript 将无法工作。此外,CSP 还可以禁用内部样式表(位于 <style> 标签内)和内联样式(使用 style 属性)。

因此,要仔细设计网站,以确保 CSP 造成的麻烦尽可能少,并且更容易实施。

CSP 还可以用于对以下内容进行精细控制:

实施 CSP 的步骤

注意: 在使用 Content-Security-Policy 标头实施任何实际的 CSP 之前,建议您首先使用 Content-Security-Policy-Report-Only HTTP 标头对其进行测试。这可以让您查看哪些违规行为将在使用该策略时发生。此测试需要使用 report-to(或已弃用的 report-uri),如下所述。

  1. 首先尝试使用 default-src https: 策略。这是一个很好的第一步,因为它禁用了内联代码,并要求浏览器在加载资源时使用 HTTPS。它还将帮助您开始确定由于策略导致无法加载的资源。 default-src 作为其他 CSP 获取指令的备用。
  2. 接下来,开始使策略更加具体,以允许您需要的项目,同时阻止任何不需要的项目。您可以首先使用一个锁定程度较高的策略来扩展策略范围,例如 default-src 'none'; form-action 'self'; img-src 'self'; object-src 'none'; script-src 'self'; style-src 'self';
  3. 然后,您可以在测试过程中添加特定来源;例如,style-src 'self' https://example.com/
  4. API 端点应使用禁用资源加载和嵌入的策略;例如,Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
  5. 对于具有大型代码库且需要大量工作才能禁用内联脚本的现有网站,您可以使用一些专为简化传统网站采用而设计的 CSP 功能。例如,nonce-* 指令要求 <script> 在其 nonce 属性中指定相同的 nonce,才能成功加载,而 script-dynamic 指令将扩展对伴随 nonce 的脚本的信任,以扩展到该顶级脚本加载的其他脚本。

请记住以下几点:

  • 如果您无法使用 Content-Security-Policy 标头,则页面可以包含 <meta http-equiv="Content-Security-Policy" content="…"> 元素。这应该是文档 <head> 中出现的第一个 <meta> 元素。
  • data: URI 需要谨慎使用,因为它们在 script-srcobject-src(或 default-src)中是不安全的。
  • 同样,对于具有 JSONP 端点的网站,使用 script-src 'self' 可能不安全。这些网站应使用包含其 JavaScript 源文件夹路径的 script-src
  • 网站应使用 report-toreport-uri 报告指令。这些指令会导致浏览器向端点 POST 关于 CSP 违规的 JSON 报告(在 report-to 的情况下,在 Reporting-Endpoints 标头中指定)。这允许快速捕获和修复 CSP 违规行为。

    注意: > report-to 比已弃用的 report-uri 更受青睐;但是,两者仍然需要,因为 report-to 尚未获得完全的跨浏览器支持。

  • 不要在 CSP 中包含任何不安全的来源。例如,在 script-src 中不要包含 unsafe-inlinedata: URI,以及过于宽泛的来源或表单提交目标。
  • 除非网站需要执行插件的能力,否则应使用 object-src 'none' 禁用插件执行。
  • 如果您通过 <use> 元素嵌入在外部文件中定义的 SVG 精灵,例如
    svg
    <svg>
      <use href="/images/icons.svg#icon"/>
    </svg>
    
    如果您设置了 default-src 'none' 策略,则您的 SVG 图像将在 Firefox 中被阻止。Firefox 不会像其他浏览器那样将 SVG 视为嵌入式图像,因此 img-src 'self' 将不允许加载它们。如果您希望外部精灵在 Firefox 中加载,则需要使用 default-src 'self'(请参阅 错误 1773976此 CSP 规范问题 以获取更多信息)。或者,如果 default-src 'none' 策略是硬性要求,您可以在 HTML 页面中内联包含 SVG 精灵 - 请参阅 CSP:default-src 以获取示例。

示例

禁用不安全的内联/eval,并且仅允许通过 HTTPS 加载资源(图像、字体、脚本等)

http
Content-Security-Policy: default-src https:

执行相同的操作,但使用 <meta> 元素

html
<meta http-equiv="Content-Security-Policy" content="default-src https:" />

禁用不安全的内联/eval,并允许除插件执行之外的所有其他操作

http
Content-Security-Policy: default-src *; object-src 'none'

禁用不安全的内联/eval,并且仅从同源加载资源,除了图像,它们可以从 https://i.imgur.com 加载。这也禁用了插件的执行

http
Content-Security-Policy: default-src 'self'; img-src 'self' https://i.imgur.com;
  object-src 'none'

禁用不安全的内联/eval 脚本和插件,仅从同源加载脚本和样式表,允许从 https://fonts.gstatic.com 加载字体,并允许从同源和 https://i.imgur.com 加载图像。网站应该以类似这样的策略为目标

http
Content-Security-Policy: default-src 'none'; font-src https://fonts.gstatic.com;
  img-src 'self' https://i.imgur.com; object-src 'none'; script-src 'self';
  style-src 'self'

允许传统网站安全地加载脚本,并通过 nonce 提供更高的信任级别

html
<script nonce="2726c7f26c">
  const inline = 1;
  // …
</script>
http
Content-Security-Policy: script-src 'strict-dynamic' 'nonce-2726c7f26c'

暂不实施策略;仅报告可能发生的违规行为

http
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"

Content-Security-Policy-Report-Only: default-src https:;
  report-uri /csp-violation-report-endpoint/;
  report-to csp-endpoint;

禁用资源加载和嵌入。API 应使用类似这样的策略

http
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'

另请参阅