WEB的内容安全策略CSP(Content-Security-Policy)
1.CSP 简介
内容安全策略(Content Security Policy,简称CSP)是一种以可信白名单作机制,来限制网站是否可以包含某些来源内容,缓解广泛的内容注入漏洞,比如 XSS。 简单来说,就是我们能够规定,我们的网站只接受我们指定的请求资源。默认配置下不允许执行内联代码(<script>块内容,内联事件,内联样式),以及禁止执行eval() , new Function() , setTimeout([string], …) 和setInterval([string], …) 。
2.CSP 使用方式
CSP可以由两种方式指定: HTTP Header 和 HTML。策略都是在服务器端设置,由浏览器执行策略。
- 通过定义在HTTP header 中使用(有时你会看到 X-Content-Security-Policy 标头,但那是旧版本,并且你无须再如此指定它):
- “Content-Security-Policy:” 策略集
- 通过定义在 HTML meta标签中使用:
- <metahttp-equiv=”content-security-policy”content=”策略集”>
策略是指定义 CSP 的语法内容。某些功能(例如发送 CSP 违规报告)仅在使用 HTTP 标头时可用。
如果 HTTP 头 与 meta 标签同时定义了 CSP,则会优先采用 HTTP 头的 。
定义后,凡是不符合 CSP策略的外部资源都会被阻止加载。
2.1 仅报告(report-only)模式
为降低部署成本,CSP 可以部署为仅报告(report-only)模式。在此模式下,CSP 策略不是强制性的,但是任何违规行为将会报告给一个指定的 URI 地址。此外,仅报告标头可以用来测试对策略未来的修订,而不用实际部署它。
你可以用 Content-Security-Policy-Report-Only HTTP 标头来指定你的策略,像这样:
Content-Security-Policy-Report-Only: policy
如果 Content-Security-Policy-Report-Only 标头和 Content-Security-Policy 同时出现在一个响应中,两个策略均有效。在 Content-Security-Policy 标头中指定的策略有强制性,而 Content-Security-Policy-Report-Only 中的策略仅产生报告而不具有强制性。
支持 CSP 的浏览器将始终对于每个企图违反你所建立的策略都发送违规报告,如果策略里包含一个有效的report-uri 指令。
3.CSP 语法
3.1 策略
每一条策略都是指令与指令值组成:
Content-Security-Policy:指令1指令值1
策略与策略之间用分号隔开,例如:
Content-Security-Policy:指令1 指令值1;指令2 指令值2;指令3 指令值3
在一条策略中,如果一个指令中有多个指令值,则指令值之间用空号隔开:
Content-Security-Policy:指令a 指令值a1指令值a2
3.2 CSP 指令
- default-src : 定义针对所有类型(js/image/css/font/ajax/iframe/多媒体等)资源的默认加载策略,如果某类型资源没有单独定义策略,就使用默认的。
- script-src : 定义针对 JavaScript 的加载策略。
- style-src : 定义针对样式的加载策略。
- img-src : 定义针对图片的加载策略。
- font-src : 定义针对字体的加载策略。
- media-src : 定义针对多媒体的加载策略,例如:音频标签<audio>和视频标签<video>。
- object-src : 定义针对插件的加载策略,例如:<object>、<embed>、<applet>。
- child-src :定义针对框架的加载策略,例如: <frame>,<iframe>。
- connect-src : 定义针对 Ajax/WebSocket 等请求的加载策略。不允许的情况下,浏览器会模拟一个状态为400的响应。
- sandbox : 定义针对 sandbox 的限制,相当于 <iframe>的sandbox属性。
- report-uri : 告诉浏览器如果请求的资源不被策略允许时,往哪个地址提交日志信息。
- form-action : 定义针对提交的 form 到特定来源的加载策略。
- referrer : 定义针对 referrer 的加载策略。
- reflected-xss : 定义针对 XSS 过滤器使用策略。
3.3 CSP 指令值
指令值 | 说明 |
* | 允许加载任何内容 |
‘none’ | 不允许加载任何内容 |
‘self’ | 允许加载相同源的内容 |
www.a.com | 允许加载指定域名的资源 |
*.a.com | 允许加载 a.com 任何子域名的资源 |
https://a.com | 允许加载 a.com 的 https 资源 |
https: | 允许加载 https 资源 |
data: | 允许加载 data: 协议,例如:base64编码的图片 |
‘unsafe-inline’ | 允许加载 inline 资源,例如style属性、onclick、inline js、inline css等 |
‘unsafe-eval’ | 允许加载动态 js 代码,例如 eval() |
4.CSP 例子
- 例子1
- 所有内容均来自网站的自己的域:
- Content-Security-Policy:default-src’self’
- 所有内容均来自网站的自己的域:
- 例子2
- 所有内容都来自网站自己的域,还有其他子域(假如网站的地址是:a.com):
- Content-Security-Policy:default-src’self’ *.a.com
- 所有内容都来自网站自己的域,还有其他子域(假如网站的地址是:a.com):
- 例子3
- 网站接受任意域的图像,指定域(a.com)的音频、视频和多个指定域(a.com、b.com)的脚本:
- Content-Security-Policy:default-src’self’;img-src *;media-src a.com;script-src a.com b.com
- 网站接受任意域的图像,指定域(a.com)的音频、视频和多个指定域(a.com、b.com)的脚本:
- 例子4
- 所有内容都来自网站自己的域,还有其他https子域(假如网站的地址是:https://www.a.com):
- Content-Security-Policy:default-src’self’ https://www.a.com
- 所有内容都来自网站自己的域,还有其他https子域(假如网站的地址是:https://www.a.com):
- 在线 CSP编写的网址:http://cspisawesome.com/
5.CSP 默认特性
- 阻止内联代码执行
- CSP除了使用白名单机制外,默认配置下阻止内联代码执行是防止内容注入的最大安全保障。这里的内联代码包括:<script>块内容,内联事件,内联样式。
- (1) script代码,<script>……<scritp>
- 对于<script>块内容是完全不能执行的。例如:
- <script>getyourcookie()</script>
- 对于<script>块内容是完全不能执行的。例如:
- (2) 内联事件。
- <ahref=””onclick=”handleClick();”></a>
- <ahref=”javascript:handleClick();”></a>
- (3) 内联样式
- <divstyle=”display:none”></div>
- 虽然CSP中已经对script-src和style-src提供了使用”unsafe-inline”指令来开启执行内联代码,但为了安全起见还是慎用”unsafe-inline”。
- EVAL相关功能被禁用
- 用户输入字符串,然后经过eval()等函数转义进而被当作脚本去执行。这样的攻击方式比较常见。于是乎CSP默认配置下,eval() , new Function() , setTimeout([string], …) 和setInterval([string], …)都被禁止运行。
- 比如:
- alert(eval(“foo.bar.baz”));
- window.setTimeout(“alert(‘hi’)”, 10);
- window.setInterval(“alert(‘hi’)”, 10);
- new Function(“return foo.bar.baz”);
- 如果想执行可以把字符串转换为内联函数去执行。
- alert(foo && foo.bar && foo.bar.baz);
- window.setTimeout(function() { alert(‘hi’); }, 10);
- window.setInterval(function() { alert(‘hi’); }, 10);
- function() { return foo && foo.bar && foo.bar.baz };
- 同样CSP也提供了”unsafe-eval”去开启执行eval()等函数,但强烈不建议去使用”unsafe-eval”这个指令。
- 比如:
- 用户输入字符串,然后经过eval()等函数转义进而被当作脚本去执行。这样的攻击方式比较常见。于是乎CSP默认配置下,eval() , new Function() , setTimeout([string], …) 和setInterval([string], …)都被禁止运行。
6.CSP 分析报告
可以用report-uri指令使浏览器发送HTTP POST请求把攻击报告以JSON格式传送到你指定的地址。接下来给大家介绍你的站点如何配置来接收攻击报告。
- 启用报告
- 默认情况下,违规报告不会发送。为了能使用违规报告,你必须使用report-uri指令,并至少提供一个接收地址。
- Content-Security-Policy:default-srcself; report-uri http://reportcollector.example.com/collector.cgi
- 如果想让浏览器只汇报报告,不阻止任何内容,可以改用Content-Security-Policy-Report-Only头。
- 违规报告语法,
- 该报告JSON对象包含以下数据:
- blocked-uri:被阻止的违规资源
- document-uri:拦截违规行为发生的页面
- original-policy:Content-Security-Policy头策略的所有内容
- referrer:页面的referrer
- status-code:HTTP响应状态
- violated-directive:违规的指令
- 违规报告例子
- http://example.com/signup.html 中CSP 规定只能加载cdn.example.com的CSS样式。
- Content-Security-Policy:default-src’none’; style-src cdn.example.com;report-uri /test/csp-report.php
- signup.html中的代码类似与这样:
- <!DOCTYPEhtml>
- <html>
- <head>
- <title>Sign Up</title>
- <linkrel=”stylesheet”href=”css/style.css”>
- </head>
- <body>… Content …</body>
- </html>
- 你能从上面的代码找出错误吗?策略是只允许加载cdn.example.com中的CSS样式。但signup.html试图加载自己域的style.css样式。这样违反了策略,浏览器会向 http://example.com/test/csp-report.php 发送POST请求提交报告,发送格式为JSON格式。
- {“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”,
- }}
- {“csp-report”: {
- 你从上面可以看到blocked-uri给出了详细的阻断地址 http://example.com/css/style.css,但也并不是每次都是这样。比如试图从 http://anothercdn.example.com/stylesheet.css 加载CSS样式时,浏览器将不会传送完整的路径,只会给出 http://anothercdn.example.com/ 这个地址。这样做是为了防止泄漏跨域的敏感信息。
- 服务端csp-report.php代码可以这样写:
- <?php
- $file = fopen(‘csp-report.txt’, ‘a’);
- $json = file_get_contents(‘php://input’);
- $csp = json_decode($json, true);
- foreach ($csp[‘csp-report’] as$key => $val) {
- fwrite($file, $key . ‘: ‘ . $val . “”);
- }
- fwrite($file, ‘End of report.’ . “”);
- fclose($file);
- ?>
7.兼容性备注
在某些版本的 Safari 网络浏览器中存在一种特殊的不兼容性,即如果设置了内容安全策略标头,但没有设置相同来源(Same Origin)标头。浏览器将阻止自我托管的内容和网站外的内容,并错误地报告说这是由于内容安全政策不允许该内容。