WEB开发面试畅谈(3)——内容安全策略CSP

什么是CSP?

CSP是由单词 Content Security Policy 的首单词组成,CSP旨在减少(注意这里是减少而不是消灭)跨站脚本攻击。

CSP是一种由开发者定义的安全性政策性申明,通过CSP所约束的的规责指定可信的内容来源(这里的内容可以指脚本、图片、iframe、fton、style等等可能的远程的资源)。通过CSP协定,让WEB处于一个安全的运行环境中。

更多关于CSP的介绍可以参考W3C的相关文档: https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#content-security-policy-header-field
一部分中文翻译:CSP - HTML5 Chinese Interest Group Wiki

CSP的讨论

CSP的出现可以一定程度上的减少XSS的攻击,但不一定意味着XSS的消失。

很多人把它喻为XSS攻击的终结者,因为这种策略不再像传统只靠各种正则和特征匹配来识别跨站攻击Payload,而是直接从协议层把一些存在安全隐患的用法默认给干掉了,把同源同域更发挥到了极致。

  1. 只允许本站资源Content-Security-Policy: default-src‘self’
  2. 允许本站的资源以及任意位置的图片以及http://trustedscripts.example.com下的脚本。Content-Security-Policy: default-src ‘self’;img-src *;script-src http://trustedscripts.example.com。
  3. CSP策略在默认的情况下是不允许使用data URIs资源的,如果要使用,那么需要显示的指定,比如:img-src ‘self’ data:
  4. script-src:在处理脚本资源的时候设置”unsafe-inline”可以阻止内联Js代码的执行。使用unsafe-eval开关可以禁止eval,setTimeout,setInterval函数的执行。
  5. object-src:控制embed,code,archive applet等对象。
  6. style-src:会控制样式表@import和rel时所引入的URI资源,设置unsafe-inline规则可以是浏览器拒绝解析内部样式和内联样式定义。并不会阻止链入外部样式表。
  7. img-src:可以控制图片资源的连接,包括img标签的src属性,以及CSS3中的url()和image()方法,以及link标签中的href属性(当rel设置成与图像相关的值,比如HTML支持的icon)。
  8. media-src:控制媒体类型的外部链入资源,如video, audio, source, 和track标签的src属性。
  9. frame-src:控制内嵌框架包含的外部页面连接:iframe or a frame。
  10. font-src:控制CSS中的@font-face
  11. connect-src:控制XMLHttpRequest中的open(),WebSocket,EventSource
  12. inline script和eval类型函数(包括eval、setInterval、setTimeout和new Function())是不被执行的。另外data URIs也是默认不允许使用的,XBL,只允许通过chrome:和resource:形式uri请求的XBL,其它的比如在CSS中通过-moz-binding来指定的XBL则不允许被执行。

早期WEB开发的规范并不明确,为了向前兼容性,最新的浏览器仍兼容着最古老而且可能存在风险的语法和接口 。通俗的讲,CSP明确地告诉我们,哪些WEB开发行为对于宽松模式来说是可以使用的,但出于安全问题我们应尽早摒弃。我们不仅要做到实现更能,而且要更好更安全地实现。

下面例举个CSP适用的实际场景,大家会一目了然CSP的约束内容:

不执行Inline JavaScript

Inline JavaScript和eval一样危险,将不会被执行。这条规则将同时禁止内嵌< script >块和内联事件(例如:< button onclick=”…” >;)。
这条规则通过禁止第三方脚本避免大量的跨脚本攻击。同时它还强制您编写内容与表现分离的代码(这本就是您该做的,对吧?)。用个例子说明这一点:假设您在一个单独的popup.html文件中实现如下Browser Action弹出页:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!doctype html>
<html>
<head>
<title>My Awesome Popup!</title>
<script>
function awesome() {
// do something awesome!
}

functiontotallyAwesome() {
// do something TOTALLY awesome!
}

functionclickHandler(element) {
setTimeout("awesome(); totallyAwesome()", 1000);
}
</script>
</head>
<body>
<button onclick="clickHandler(this)">
Click for awesomeness!
</button>
</body>
</html>

为了使它能以您所希望的方式运行,结合本规则,需要做以下三个方面的修改:

  • clickHandler的定义需移到外部JavaScript文件中(如:popup.js)。
  • 内联事件需通过addEventListener,并移到外部popup.js文件中实现。
  • 为了避免使用转换字符串到JavaScript的函数”awesome(); totallyAwesome()”的执行,setTimeout调用需要重写。

修改之后将会像下面这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
popup.js:

function awesome() {
// Do something awesome!
}

functiontotallyAwesome() {
// do something TOTALLY awesome!
}

functionawesomeTask() {
awesome();
totallyAwesome();
}


functionclickHandler(e) {
setTimeout(awesomeTask, 1000);
}

// Add event listeners once the DOM has fully loaded by listening for the

// `DOMContentLoaded` event on the document, and adding your listeners to

// specific elements when it triggers.

document.addEventListener('DOMContentLoaded', function () {
document.querySelector('button').addEventListener('click', clickHandler);
});



popup.html:

<!doctype html>
<html>
<head>
<title>My Awesome Popup!</title>
<script src="popup.js"></script>
</script>
</head>
<body>
<button>Click for awesomeness!</button>
</body>
</html>

综述

首先CSP是可以在一定程度上提高XSS的攻击难度的,甚至杜绝XSS,前提是CSP策略用的好。还要考虑CSP能否普及,因为CSP在提供安全性的同时也提高了前端逻辑的复杂度,很多资源需要调整,类似QQ,新浪,搜狐这样的站群,想通过合适的CSP同时提高安全性和易用性是很难的。但是我们仍要尽可能使用更加规范的编码方式提高WEB开发的质量。

目前CSP规范并未应用于全部浏览器,仅仅新浏览器会得到支持。但是遵循CSP规范开发的WEB应用将获得更好的代码结构和用户体验,大大提高安全性。这可能会彻底改变你之前的开发习惯和方式。甚至你会恍然发现,之前的代码是如此脆弱不堪。甚至会有些不适。但是,时代总是向前发展的。当你渐渐习惯并接受了它,才发现它带来的无尽好处,助你在web开发的道路上走得更快更好。