2022/10/26 CSP内容安全策略和jquery的坑

1,069 阅读2分钟

CSP内容安全策略

关于csp的入门介绍,在 Content Security Policy 入门教程中已经介绍的非常清晰明了了,所以在这边仅仅是做一个试验,用 meta 标签和最常用的 script-src 选项写一个demo

  1. 使用最基本的script-src 'self';限制所有的外部资源,都只能从当前域名加载。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" 
http-equiv="Content-Security-Policy" 
content="script-src 'self';">
<title>phoeny</title>
</head>
<body>
<p id="test2">使用script马上弹出hi</p>
<p id="test3">使用img马上弹出123</p>
<script>
    var dom = document.getElementById('test2')
    dom.innerHTML= "<p>我的第二个段落。</p><script>alert('hi')<\/script>"
</script>
<script>
    var dom = document.getElementById('test3')
    dom.innerHTML= "<img src='x' onerror='alert(123)'/>"
</script>
</body>
</html>

根据我们所想,结果应该是立刻弹出123,但不会弹出 hi,因为HTML5 中指定不执行由 innerHTML 插入的 <script>,这个话题在 22/10/12 xss攻击中提过。
但实际上它报错了:

image.png 报错内容告诉我们,csp 限制了这两个标签的执行,要加上一些配置才可以,所以我们试着将 meta 标签改成

<meta charset="utf-8" 
http-equiv="Content-Security-Policy" 
content="script-src 'self' 'unsafe-inline' ;">

再次运行,完美契合了我们最初的想法,也就是允许运行了unsafe-inline不安全的行内脚本。

  1. 让我们试一试 appendChild
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" 
http-equiv="Content-Security-Policy" 
content="script-src 'self' 'unsafe-inline';">
<title>phoeny</title>
</head>
<body>
<p id="test">使用append马上弹出xss</p>
<script>
    var dom = document.getElementById('test')
    var sc = document.createElement('script')
    sc.innerHTML=alert('xss')
    dom.appendChild(sc)
</script>
</body>
</html>

可以成功弹出 xss,也就是说用appendChild可以避开html5对于script的安全处理。

  1. 当我试着用 jquery 来实现这一切的时候,结果又看不懂了!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" 
http-equiv="Content-Security-Policy" 
content="script-src 'self' 'unsafe-inline'">
<title>phoeny</title>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
</head>
<body>
<p id="test0">点我!使用jquery弹出haha</p>
<p id="test1">我会被jquery替换</p>
<script>
$(document).ready(function(){
    $("#test0").click(function(){
        $("#test1").html("<script>alert('haha')<\/script>")
    });
});
</script>
</body>
</html>

首先,它会报错:

image.png 这是因为它无法加载 jquery,我们很快会想到将这个域名加到白名单中,也就是将将 meta 标签改成

<meta charset="utf-8" 
http-equiv="Content-Security-Policy" 
content="script-src 'self' 'unsafe-inline' https://cdn.staticfile.org">

ok,没有报错啦。

已知,jquery中是用appendChild添加script标签的,所以我们合理猜测,点击之后会弹出haha。但是,当我们真的点击test0时,又又又报错了:

image.png

这个报错真的是百思不得其解,和 eval 又有什么关系呢,好吧,原来jquery中的html方法的实现有些与众不同,jquery在找到 script 标签的时候,会先将其取出来不运行,推进一个列表中,最后循环这个列表用 eval 再去运行...

所以我们为了让它运行,不得不加一个 unsafe-eval

<meta charset="utf-8" 
http-equiv="Content-Security-Policy" 
content="script-src 'self' 'unsafe-inline' https://cdn.staticfile.org 'unsafe-eval'">

而且,这只有在低版本的jquery才会出现,当我们将jquery的版本换到更高,就和我们预料的结果是一样的。

<script src="https://cdn.staticfile.org/jquery/3.6.1/jquery.min.js"></script>

好啦,这就是在试验 csp 的时候踩得一些坑呀,还真是挺麻烦哒!