Content Security Policy

2017-02-10 13:19 #旧文章

本文以 MDN 为参考

CSP 兼容旧的浏览器,即在不支持它的浏览器上依旧能够正常地加载网页。如果使用不支持它的浏览器加载网站,浏览器则会使用标准的同源策略。

启用 CSP,你需要设置你的网络服务器使其返回一个 Content-Security-PolicyHTTP header(有时候你可能会看见有提及 X-Content-Security-Policy 的文章,但是这是一个过去的版本,所以你不必设置这个 Header)。

另外,你也可以使用 <meta> 标签来配置 CSP,例如:<meta http-equiv="Content-Security-Policy" content="default-src 'self'; image-src https:*; child-src 'none';">

优势

防止大部分 XSS 跨站脚本攻击

使用 CSP 的最大好处就是可以有效地防止并且上报绝大多数 XSS 攻击。XSS 攻击利用了浏览器信任从服务器上获取的数据这一特点。恶意脚本会被受害者的浏览器所执行,因为浏览器信任从服务器上获取到的内容,即使看起来不是从正常的地方来的。

CSP 通过指定浏览器应该从哪一个域名加载合法的脚本,为减少或消除 XSS 提供了可能。兼容 CSP 的浏览器只会执行从 CSP 信任的来源获取到的脚本,并将其他的脚本忽略(包括内联脚本和 HTML 事件处理标记)。

一个终极的保护方法是禁用所有的脚本执行。(这句话不是我说的啊)

防止包嗅探攻击

除了限制可以加载资源的域名,服务器也可以指定应该使用哪一种协议,例如,并且从安全角度推荐,指定所有的资源必须通过 HTTPS 进行加载,这是最吼的。一个完整的数据传输安全策略不仅包括了强制使用 HTTPS,还包括了为所有 cookies 设置 secure flag 并且能够使页面自动从 HTTP 跳转到 HTTPS。同时,网站还应该使用 HSTS 来确保浏览器只使用加密的通道进行连接。

使用 CSP

配置 CSP 时需要为网页添加 Content-Security-PolicyHTTP header,并且指定一个值来控制资源的加载。例如,一个上传并且显示图片的页面可以允许从所有地方加载图片,但限制表单提交的地址。一个适当的 CSP 可以很好地保护页面免遭 XSS 攻击。这篇文章介绍了如何正确地构造这样的 headers,并且附有示例代码。

指定你的策略

你可以使用 CSP 的 HTTP header 来指定你的策略,像这样:

Content-Security-Policy: policy

其中的 policy 是一个描述策略的字符串指令。

制定策略

策略通过一系列字段来描述,每一个字段都包含了资源类型或策略范围。你的策略应该包含一个用于未被指明资源类型的 default-src 字段。一个策略需要包含一个 default-srcscript-src 字段来阻止内联脚本的运行,也可以阻止 eval()的使用。同时,一个策略需要包含一个 default-srcstyle-src 字段来阻止从 <style> 或者 style 属性加载样式。

实例:常见的用法

这一节提供了一些常见的策略方案。

实例 1

网站管理员希望所有的资源都从当前站点加载(但是这不包括子域名)

Content-Security-Policy: default-src 'self'

实例 2

网站管理员希望从一个信任的域名及其子域名加载资源(与当前网站不同源的域名)

Content-Security-Policy: default-src 'self' *.trusted.com

实例 3

网站管理员希望允许用户从任何来源引用图片,但是音频 / 视频只能从信任的提供方获取,而且所有的脚本只能从指定服务器获取。

Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com

在这里,默认地,内容只能从当前来源加载,除了下面的例外:

  • 图片可以从任何地方加载(注意 * 通配符)
  • 媒体只允许从 media1.com 和 media2.com 上获取(并且不包括子域名)
  • 可执行脚本只允许从 userscripts.example.com 加载

实例 4

网银的网站管理员希望确保所有的内容都通过 SSL 进行加载以防攻击者窃听网络流量。

Content-Security-Policy: default-src https://onlinebanking.jumbobank.com

这样只允许通过 HTTPS 从 onlinebanking.jumbobank.com 加载内容。

实例 5

邮箱的网站管理员希望允许邮件中的 HTML,同时图片可以从任意地方获取,但不能有 JavaScript 或者其他潜在的不安全因素。

Content-Security-Policy: default-src 'self' *.mailsite.com; img-src *

注意这个例子没有指定 script-src。通过这一个例子,站点使用的是 default-src,这意味着所有脚本只能从当前站点获取。

测试你的策略

CSP 可以被设置为 report-only 模式。这个策略不会被执行,但是这样可以将所有浏览器拦截到的攻击报告至提供的 URI。另外,使用 report-only 模式还可以方便新策略的测试。

你可以设置 Content-Security-Policy-Report-Only HTTP header 来指定你的策略,像这样:

Content-Security-Policy-Report-Only: policy

如果 Content-Security-Policy-Report-OnlyContent-Security-Policy Header 在同一个相应中出现,两种策略都会被采用。这时候 Content-Security-Policy 会被执行,而 Content-Security-Policy-Report-Only 只是指定了报告的条件,并不会影响页面的加载。

支持 CSP 的浏览器一般都会将检测到的攻击报告给服务器,但你的策略必须指定一个 report-uri 字段。

启用攻击报告

默认地,攻击不会被报告。想要启用攻击报告,你需要在策略中指定 report-uri 字段,提供至少一个处理报告的 URI:

Content-Security-Policy: default-src 'self'; report-uri http://reportcollector.example.com/collector.cgi

然后你需要配置你的服务器来处理这些报告。

攻击报告的语法

攻击报告是一个 JSON 对象,包含了以下字段:

document-uri
攻击发生所在文档的URI。
referrer
攻击发生所在文档的referrer。
blocked-uri
被CSP所阻止的加载请求URI。如果被阻止的URI与当前文档非同源,则这个URI会被缩短,只包含协议、主机和端口。
violated-directive
攻击被哪一个字段所阻止。
original-policy
原始的被HTTP header指定的CSP策略。

攻击报告实例

让我们看看一个位于 http://example.com/signup.html 的页面。它使用了如下的策略,只允许从 cdn.example.com 上加载样式表:

Content-Security-Policy: default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports

HTML 的代码如下

<!doctype html>
<html>
<head>
<title>Sign Up</title>
<link
rel="stylesheet"
href="css/style.css"
/>
</head>
<body>
... Content ...
</body>
</html>

你能发现哪里有错误吗?样式表只被允许从 cdn.example.com 上加载,然而这个网页希望从自己的源(http://example.com)上加载。捕获到这一“攻击”的浏览器会向提供的地址通过 POST 请求发送攻击报告,像这样:

{
"csp-report": {
"document-uri": "http://example.com/signup.html",
"referrer": "",
"blocked-uri": "http://example.com/css/style.css",
"violated-directive": "style-src cdn.example.com",
"original-policy": "default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports"
}
}

所见即所得,这个报告包含了被阻止的 URI。但这并不一定是这样的,当试图从 http://anothercdn.example.com/stylesheet.css 加载样式表时,报告不会发送完整的路径,报告中只会有协议、主机和端口(http://anothercdn.example.com)。W3 给出了这一[奇怪特性的解释](http://www.w3.org/TR/CSP/#violation-reports)。总之,这防止了向非同源地址发送请求时泄露敏感信息。

浏览器兼容性

桌面系统

特性ChromeEdgeFireFoxIEOperaSafariServo
Content-Security-Policy2511423.021031574?
Content-Security-Policy-Report-Only251423.010157?
  1. 在 Chrome 14 中通过 X-Webkit-CSP 实现。
  2. 在 FireFox 4 中通过 X-Content-Security-Policy 实现。
  3. 通过 X-Content-Security-Policy 实现,只支持 sandbox 字段。
  4. 在 Safari 6 中通过 X-Webkit-CSP 实现。

手机系统

特性AndroidChrome For AndroidEdge MobileFireFox for AndroidIE MobileOpera MobileSafari Mobile
Content-Security-Policy4.4(Yes)(Yes)23.0??7.11
Content-Security-Policy-Report-Only4.4(Yes)(Yes)23.0??7.1
  1. 在 IOS 5.1 中通过 X-Webkit-CSP 实现。

参考资料

(^o^)/~ 本文已完成。