内容安全策略 (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 还可以帮助解决其他问题,这些问题在其他文章中有所介绍。
- 防止点击劫持 通过阻止您的网站嵌入
<iframe>
元素。这是使用 CSPframe-ancestors
指令实现的。 - 防止 中间人 (MiTM) 攻击,通过将任何 HTTP 连接升级到 HTTPS。CSP
upgrade-insecure-requests
指令可以帮助实现这一点。有关详细信息,请参阅 HSTS 实现。
解决方案
实施强大的 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
),如下所述。
- 首先尝试使用
default-src https:
策略。这是一个很好的第一步,因为它禁用了内联代码,并要求浏览器在加载资源时使用 HTTPS。它还将帮助您开始确定由于策略导致无法加载的资源。default-src
作为其他 CSP 获取指令的备用。 - 接下来,开始使策略更加具体,以允许您需要的项目,同时阻止任何不需要的项目。您可以首先使用一个锁定程度较高的策略来扩展策略范围,例如
default-src 'none'; form-action 'self'; img-src 'self'; object-src 'none'; script-src 'self'; style-src 'self';
。 - 然后,您可以在测试过程中添加特定来源;例如,
style-src 'self' https://example.com/
。 - API 端点应使用禁用资源加载和嵌入的策略;例如,
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
。 - 对于具有大型代码库且需要大量工作才能禁用内联脚本的现有网站,您可以使用一些专为简化传统网站采用而设计的 CSP 功能。例如,
nonce-*
指令要求<script>
在其nonce
属性中指定相同的 nonce,才能成功加载,而script-dynamic
指令将扩展对伴随 nonce 的脚本的信任,以扩展到该顶级脚本加载的其他脚本。
请记住以下几点:
- 如果您无法使用
Content-Security-Policy
标头,则页面可以包含<meta http-equiv="Content-Security-Policy" content="…">
元素。这应该是文档<head>
中出现的第一个<meta>
元素。 data:
URI 需要谨慎使用,因为它们在script-src
和object-src
(或default-src
)中是不安全的。- 同样,对于具有 JSONP 端点的网站,使用
script-src 'self'
可能不安全。这些网站应使用包含其 JavaScript 源文件夹路径的script-src
。 - 网站应使用
report-to
和report-uri
报告指令。这些指令会导致浏览器向端点POST
关于 CSP 违规的 JSON 报告(在report-to
的情况下,在Reporting-Endpoints
标头中指定)。这允许快速捕获和修复 CSP 违规行为。注意: >
report-to
比已弃用的report-uri
更受青睐;但是,两者仍然需要,因为report-to
尚未获得完全的跨浏览器支持。 - 不要在 CSP 中包含任何不安全的来源。例如,在
script-src
中不要包含unsafe-inline
或data:
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 加载资源(图像、字体、脚本等)
Content-Security-Policy: default-src https:
执行相同的操作,但使用 <meta>
元素
<meta http-equiv="Content-Security-Policy" content="default-src https:" />
禁用不安全的内联/eval,并允许除插件执行之外的所有其他操作
Content-Security-Policy: default-src *; object-src 'none'
禁用不安全的内联/eval,并且仅从同源加载资源,除了图像,它们可以从 https://i.imgur.com
加载。这也禁用了插件的执行
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
加载图像。网站应该以类似这样的策略为目标
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 提供更高的信任级别
<script nonce="2726c7f26c">
const inline = 1;
// …
</script>
Content-Security-Policy: script-src 'strict-dynamic' 'nonce-2726c7f26c'
暂不实施策略;仅报告可能发生的违规行为
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 应使用类似这样的策略
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'