CSP
内容安全策略(Content Security Policy,CSP),是一个附加的安全层,有助于检测并缓解某些类型的攻击,包括跨站脚本(XSS)和数据注入攻击。
CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成。
两种方法可以启用 CSP。一种是通过 HTTP 头信息的 Content-Security-Policy 的字段。
1 | Content-Security-Policy: default-src 'self'; script-src 'self' www.test.com; |
另一种是通过 <meta>
标签。
1 | <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self';"> |
CSP 提供了很多限制选项:
限制选项 | 说明 |
---|---|
default-src | 资源默认加载策略 |
script-src | 外部脚本 |
connect-src | HTTP连接,如 XHR、WebSockets、EventSource等 |
style-src | 样式表 CSS |
img-src | 图片 |
media-src | 媒体,如 audio、video |
font-src | 字体 |
frame-src | 嵌入的外部资源,如 frame、iframe、embed、applet |
每个限制选项可以设置以下几种值,这些值就构成了白名单:
值 | 说明 |
---|---|
主机名 | https://example.com:80 http://*.test.com |
协议名 | https: data: |
‘self’ | 允许加载同域资源,需要加引号 |
‘none’ | 禁止加载任何外部资源,需要加引号 |
* | 通配符,允许除 data: blob: filesystem: 协议之外的任意 URL |
除了常规值,script-src 还可以设置一些特殊值:
值 | 说明 |
---|---|
‘unsafe-inline’ | 允许执行页面内嵌的 script 标签和事件监听函数 |
‘unsafe-eval’ | 允许将字符串当作代码执行,如eval、setTimeout、setInterval和Function等函数 |
nonce | 每次HTTP回应给出一个授权token,页面内嵌脚本必须有这个token,才会执行 |
hash | 列出允许执行的脚本代码的Hash值,页面内嵌脚本的哈希值只有吻合的情况下,才能执行 |
dynamic | 可信js生成的js代码是可信的 |
含有 nonce 值的 CSP:
1 | Content-Security-Policy: script-src 'nonce-d87adgi2' |
页面内嵌脚本,必须有这个 token 才能执行。
1 | <script nonce=d87adgi2> alert(1) </script> |
含有 hash 值的 CSP:
1 | Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng=' |
下面的代码才会允许执行,因为 sha256 值相符(不包括标签)。
1 | <script>alert('Hello, world.');</script> |
值得注意的是,同源策略是可以发送请求只是浏览器拦截了,而CSP禁止加载的资源,是连请求都被拦截的,根本发不出去。
Bypass
跳转
无论有怎样的限制,跳转总是可以实现的。当然 window 和 location 是 js 脚本需要 script-src unsafe-inline
。
1 | <meta http-equiv="refresh" content="1;http://xxx.com"> |
link标签预加载
当CSP为 default-src 'self'; script-src 'self';
时,由于 CSP 对 link 标签的预加载功能考虑不完善。在旧版本 Chrome下,可以使用 prefetch 发送数据。
1 | <link rel="prefetch" href="http://xxx.com"> |
这样没法将 cookie 带出,因此如果 script-src unsafe-inline
的条件下,可以动态生成标签带出 cookie。
1 | <script> |
当然新版本的 Chrome 会拒绝预加载。
值得一提的是,dns-prefetch 是最新版的 FireFox 和 Chrome 还可以使用的发送数据方法,这个方法是不受 default-src: self
CSP 影响的。
dns-prefetch(DNS 预解析)允许浏览器在后台提前将资源的域名转换为 IP 地址,当用户访问该资源时就可以加快 DNS 解析。
1 | <link rel="dns-prefetch" href="http://xxx.com"> |
同样在 script-src unsafe-inline
的条件下,可以动态生成标签带出 cookie。
1 | <script> |
因为域名的命名规则是 [.-a-zA-Z0-9]+
,所以需要对一些特殊字符进行替换。这样就可以在 DNS 平台上收到 DNS 解析的记录了:
除此之外,rel 还有 preconnect、preload 等。preload 需要 CSP 中有 default-src: connect-src *
才行。
跨域请求
提交表单、点击链接等跨域行为是不受 CSP 限制的。还有一些跨域请求比如 window.postMessage 也是可以利用的。
0CTF 2018 预选赛中的 h4xors.club2:
location.hash
如果js存在操作 location.hash 导致的 xss,那么这样的攻击请求不会经过后台,那么 nonce 后的随机值就不会刷新。见 lorexxar 师傅的文章:
文件上传
如果有上传点可以控制,那么可以在其中夹杂js代码,然后引用该文件完成执行。