这就是XSS跨站脚本攻击,通过虚假内容和诱骗点击来绕过同源策略。这是一个很大的问题,如果攻击者成功注入代码,有相当多的用户数据会被泄漏。
现在我们介绍一个全新的、有效的安全防御策略来减轻这种风险,这就是内容安全策略(ContentSecurity Policy,CSP)。
来源白名单
XSS攻击的核心是利用了浏览器无法区分脚本是被第三方注入的,还是真的是你应用程序的一部分。例如Google +1按钮会从https://apis.google.com/js/plusone.js加载并执行代码,但是我们不能指望从浏览器上的图片就能判断出代码是真的来自apis.google.com,还是来自apis.evil.example.com。浏览器下载并执行任意代码的页面请求,而不论其来源。
CSP定义了Content-Security-PolicyHTTP头来允许你创建一个可信来源的白名单,使得浏览器只执行和渲染来自这些来源的资源,而不是盲目信任服务器提供的所有内容。即使攻击者可以找到漏洞来注入脚本,但是因为来源不包含在白名单里,因此将不会被执行。
以上面Google +1按钮为例,因为我们相信apis.google.com提供有效的代码,以及我们自己,所以可以定义一个策略,允许浏览器只会执行下面两个来源之一的脚本。
Content-Security-Policy:
是不是很简单?
一旦我们定义了这个策略,浏览器会在检测到注入代码时抛出一个错误(请注意是什么浏览器)。
内容安全策略适用于所有常用资源
虽然脚本资源是最明显的安全隐患,但是CSP还提供了一套丰富的指令集,允许页面控制加载各种类型的资源,例如如下的类型:
content-src:限制连接的类型(例如XHR、WebSockets和EventSource)
font-src:控制网络字体的来源。例如可以通过font-src https://themes.googleusercontent.com来使用Google的网络字体。
img-src:定义了可加载图像的来源。
media-src:限制视频和音频的来源。
object-src:限制Flash和其他插件的来源。
style-src:类似于
默认情况下,所有的设置都是打开的,不做任何限制。你可以以分号分隔多个指令,但是类似于
例如,你有一个应用需要从内容分发网络(CDN,例如https://cdn.example.net)加载所有的资源,并且知道不需要任何
Content-Security-Policy:default-src https://cdn.example.net;
细节
我在例子里使用的HTTP头是Content-Security-Policy,但是现代浏览器已经通过前缀来提供了支持:Firefox使用x-Content-Security-Policy,WebKit使用X-WebKit-CSP。未来会逐步过渡到统一的标准。
策略可以根据每个不同的页面而设定,这提供了很大的灵活度。因为你的站点可能有的页面有Google +1的按钮,而有的则没有。
每个指令的来源列表可以相当灵活,你可以指定模式(data:, https:),或者指定主机名在一个范围(example.com,它匹配主机上的任意来源、任意模式和任意端口),或者指定一个完整的URI(https://example.com:443,特指https协议,example.com域名,443端口)。
你在来源列表中还可以使用四个关键字:
“none”:你可能期望不匹配任何内容
“self”:与当前来源相同,但不包含子域
“unsafe-inline”:允许内联
“unsafe-eval”:允许文本到JS的机制例如eval
请注意,这些关键词需要加引号。
沙箱
这里还有另外一个值得讨论的指令:sandbox。和其他指令有些不一致,它主要是控制页面上采取的行为,而不是页面能够加载的资源。如果设置了这个属性,页面就表现为一个设置了sandbox属性的
有害的内联代码
CSP基于来源白名单,但是它不能解决XSS攻击的最大来源:内联脚本注入。如果攻击者可以注入包含有害代码的
这个禁止项不仅包括脚本中嵌入的
<
function doAmazingThings() {
alert('YOU AM AMAZING!');
}
</
<button onclick='doAmazingThings();'>Am I amazing?</button>
重写为下面的形式:
<!-- amazing.html -->
<
<button id='amazing'>Am I amazing?</button>
// amazing.js
function doAmazingThings() {
alert('YOU AM AMAZING!');
}
document.addEventListener('DOMContentReady', function () {
document.getElementById('amazing')
.addEventListener('click', doAmazingThings);
});
无论是否使用CSP,以上的代码其实有更大的优点。内联
内联样式需要以同样的方式进行处理,无论是style属性还是style标签都需要提取到外部样式表中。这样可以防止各式各样神奇的数据泄漏方式。
如果你必须要有内联脚本和样式,可以为
Eval
即便攻击者不能直接注入脚本,他可能会诱使你的应用把插入的文本转换为可执行脚本并且自我执行。eval() , newFunction() , setTimeout([string], ...) 和setInterval([string], ...) 都可能成为这种危险的载体。CSP针对这种风险的策略是,完全阻止这些载体。
这对你构建应用的方式有一些影响:
通过内置的JSON.parse解析JSON,而不依靠eval。IE8以后的浏览器都支持本地JSON操作,这是完全安全的。
通过内联函数代替字符串来重写你setTimeout和setInterval的调用方式。例如:
setTimeout("document.querySelector('a').style.display = 'none';", 10);
可以重写为:
setTimeout(function () { document.querySelector('a').style.display = 'none'; }, 10);
避免运行时的内联模版:许多模版库都使用new Function()以加速模版的生成。这对动态程序来说非常棒,但是对恶意文本来说存在风险。
报告
CSP可以在服务器端阻止不可信的资源对用户来说非常有用,但是对于获取各种发送到服务器的通知来说对我们却非常有用,这样我们就能识别和修复任何恶意脚本注入。为此你可以通过report-uri指令指示浏览器发送JSON格式的拦截报告到某个地址。
Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;
报告看起来会像下面这样:
{
"csp-report": {
"document-uri": "http://example.org/page.html",
"referrer": "http://evil.example.com/",
"blocked-uri": "http://evil.example.com/evil.js",
"violated-directive": "
"original-policy": "
}
}
其中包含的信息会帮助你识别拦截的情况,包括拦截发生的页面(document-uri),页面的referrer,违反页面策略的资源(blocked-uri),所违反的指令(violated-directive)以及页面所有的内容安全策略(original-policy)。
现实用法
CSP现在在Chrome 16+和Firefox 4+的浏览器上可用,并且它在IE10上预计会获得有限的支持。Safari目前还不支持,但是WebKit每晚构建版已经可用,所以预计Safari将会在下面的迭代中提供支持。
下面让我们看一些常用的用例:
实际案例1:社会化媒体widget
Google +1 button包括来自https://apis.google.com的脚本,以及嵌入自https://plusone.google.com的i
Facebook的Like按钮有许多种实现方案。我建议你坚持使用i
Twitter的Tweet按钮依赖于
其它的平台有相似的情况,可以类似的解决。我建议把default-src设置为none,然后查看控制台来检查你需要使用哪些资源来确保widget正常工作。
使用多个widget非常简单:只需要合并所有的策略指令,记住把同一指令的设置都放在一起。如果你想使用上面这三个widget,策略看起来会像下面这样:
实际案例2:防御
假设你访问一个银行网站,并且希望确保只加载你所需的资源。在这种情况下,开始设置一个默认的权限来阻止所有的内容(default-src ‘none’),并且从这从头构建策略。
比如,银行网站需要从来自https://cdn.mybank.net的CDN加载图像、样式和脚本,并且通过XHR连接到https://api.mybank.com/来拉取各种数据,还需要使用
Content-Security-Policy: default-src 'none';
实际案例3:只用SSL
一个婚戒论坛管理员希望所有的资源都通过安全的方式进行加载,但是不想真的编写太多代码;重写大量第三方论坛内联脚本和样式的代码超出了他的能力。所以以下的策略将会是非常有用的:
Content-Security-Policy: default-src https:;
尽管default-src指定了https,脚本和样式不会自动继承。每个指令将会完全覆盖默认资源类型。
未来
W3C的Web应用安全工作组正在制定内容安全策略规范的细节,1.0版本将要进入最后修订阶段,它和本文描述的内容已经非常接近。而public-webappsec@邮件组正在讨论1.1版本,浏览器厂商也在努力巩固和改进CSP的实现。
CSP 1.1在画板上有一些有趣的地方,值得单独列出来:
通过
你甚至可以在运行时通过脚本来添加策略。
DOM API:如果CSP的下一个迭代添加了这个特性,你可以通过
新的指令:许多新指令正在讨论中,包括
如果你对这些未来特性的讨论感兴趣,可以阅读邮件列表的归档或者加入邮件列表。
本文译自:http://www.html5rocks.com/en/tutorials/security/content-security-policy/
摘自:蒋宇捷的博客