前言:HTML5出现后,网络安全更加受到广泛的关注。Web对于网络安全有哪些改进?我们如何来面对越来越危险的网络欺诈和攻击?下面的文章谈到了W3C对于这个问题的最新解决方案。未来有机会的话,我会针对XSS、P3P、同源策略、CORS(跨域资源共享)和CSP进行关于HTML5内容安全的现场分享。
------------------------华丽的分界线
注意:本文所讨论的API还未最终确定,请在自己的项目中谨慎使用。
万维网的安全策略植根于[同源策略](http://en.wikipedia.org/wiki/Same_origin_policy)。例如[http://blog.csdn.net/hfahe](http://blog.csdn.net/hfahe)的代码只能访问[http://blog.csdn.net](http://blog.csdn.net/)的数据,而没有访问[http://www.baidu.com](http://www.baidu.com/)的权限。每个来源都与网络的其它部分分隔开,为开发人员构建了一个安全的沙箱。理论上这是完美的,但是现在攻击者已经找到了聪明的方式来破坏这个系统。
这就是[XSS跨站脚本攻击](http://en.wikipedia.org/wiki/Cross-site_scripting),通过虚假内容和诱骗点击来绕过同源策略。这是一个很大的问题,如果攻击者成功注入代码,有相当多的用户数据会被泄漏。
现在我们介绍一个全新的、有效的安全防御策略来减轻这种风险,这就是**内容安全策略**(ContentSecurity Policy,CSP)。
**来源白名单**
XSS攻击的核心是利用了浏览器无法区分脚本是被第三方注入的,还是真的是你应用程序的一部分。例如Google +1按钮会从[https://apis.google.com/js/plusone.js](https://apis.google.com/js/plusone.js)加载并执行代码,但是我们不能指望从浏览器上的图片就能判断出代码是真的来自apis.google.com,还是来自apis.evil.example.com。浏览器下载并执行任意代码的页面请求,而不论其来源。
![](https://box.kancloud.cn/2016-08-09_57a9a2e8157e9.jpg)
CSP定义了Content-Security-PolicyHTTP头来允许你创建一个可信来源的白名单,使得浏览器只执行和渲染来自这些来源的资源,而不是盲目信任服务器提供的所有内容。即使攻击者可以找到漏洞来注入脚本,但是因为来源不包含在白名单里,因此将不会被执行。
以上面Google +1按钮为例,因为我们相信apis.google.com提供有效的代码,以及我们自己,所以可以定义一个策略,允许浏览器只会执行下面两个来源之一的脚本。
Content-Security-Policy:script-src 'self' [https://apis.google.com](https://apis.google.com/)
是不是很简单?script-src可以为指定页面控制脚本相关权限。这样浏览器只会下载和执行来自[http://apis.google.com](http://apis.google.com/)和本页自身的脚本。
一旦我们定义了这个策略,浏览器会在检测到注入代码时抛出一个错误(请注意是什么浏览器)。
**内容安全策略适用于所有常用资源**
虽然脚本资源是最明显的安全隐患,但是CSP还提供了一套丰富的指令集,允许页面控制加载各种类型的资源,例如如下的类型:
- content-src:限制连接的类型(例如XHR、WebSockets和EventSource)
- font-src:控制网络字体的来源。例如可以通过font-src [https://themes.googleusercontent.com](https://themes.googleusercontent.com/)来使用Google的网络字体。
- frame-src:列出了可以嵌入的frame的来源。例如frame-src https://youtube.com只允许嵌入YouTube的视频。。
- img-src:定义了可加载图像的来源。
- media-src:限制视频和音频的来源。
- object-src:限制Flash和其他插件的来源。
- style-src:类似于Script-src,只是作用于css文件。
默认情况下,所有的设置都是打开的,不做任何限制。你可以以分号分隔多个指令,但是类似于script-src [https://host1.com](https://host1.com/);script-src[https://host2.com](https://host2.com/)的形式,第二个指令将会被忽略。正确的写法是script-src [https://host1.com](https://host1.com/)[https://host2.com](https://host2.com/)。
例如,你有一个应用需要从内容分发网络(CDN,例如https://cdn.example.net)加载所有的资源,并且知道不需要任何frame和插件的内容,你的策略可能会像下面这样:
~~~
Content-Security-Policy:default-src https://cdn.example.net; frame-src 'none'; object-src 'none'
~~~
**细节**
我在例子里使用的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/),特指https协议,example.com域名,443端口)。
你在来源列表中还可以使用四个关键字:
- “none”:你可能期望不匹配任何内容
- “self”:与当前来源相同,但不包含子域
- “unsafe-inline”:允许内联Javascript和CSS
- “unsafe-eval”:允许文本到JS的机制例如eval
请注意,这些关键词需要加引号。
**沙箱**
这里还有另外一个值得讨论的指令:sandbox。和其他指令有些不一致,它主要是控制页面上采取的行为,而不是页面能够加载的资源。如果设置了这个属性,页面就表现为一个设置了sandbox属性的frame一样。这对页面有很大范围的影响,例如防止表单提交等。这有点超出了本文的范围,但是你可以在[HTML5规范的“沙箱标志设置”章节](http://www.whatwg.org/specs/web-apps/current-work/multipage/origin-0.html#sandboxing-flag-set)找到更多信息。
**有害的内联代码**
CSP基于来源白名单,但是它不能解决XSS攻击的最大来源:内联脚本注入。如果攻击者可以注入包含有害代码的script标签(<script>sendMyDataToEvilDotCom();</script>),浏览器并没有好的机制来区分这个标签。CSP只能通过完全禁止内联脚本来解决这个问题。
这个禁止项不仅包括脚本中嵌入的script标签,还包括内联事件处理程序和javascrpt:这种URL。你需要把script标签的内容放到一个外部文件里,并且用适当的addEventListener的方式替换javascript:和<a… onclick=”[JAVASCRIPT]”>。例如,你可能会把下面的表单:
~~~
<script>
function doAmazingThings() {
alert('YOU AM AMAZING!');
}
</script>
<button onclick='doAmazingThings();'>Am I amazing?</button>
~~~
重写为下面的形式:
~~~
<!-- amazing.html -->
<script src='amazing.js'></script>
<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,以上的代码其实有更大的优点。内联JavaScript完全混合了结构和行为,你不应该这么做。另外外联资源更容易进行浏览器缓存,开发者更容易理解,并且便于编译和压缩。如果采用外联代码,你会写出更好的代码。
内联样式需要以同样的方式进行处理,无论是style属性还是style标签都需要提取到外部样式表中。这样可以防止各式各样[神奇的数据泄漏方式](http://scarybeastsecurity.blogspot.com/2009/12/generic-cross-browser-cross-domain.html)。
如果你必须要有内联脚本和样式,可以为script-src or style-src属性设定'unsafe-inline值。但是不要这样做,禁止内联脚本是CSP提供的最大安全保证,同时禁止内联样式可以让你的应用变得更加安全和健壮。这是一个权衡,但是非常值得。
**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": "script-src 'self' https://apis.google.com",
"original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
}
}
~~~
其中包含的信息会帮助你识别拦截的情况,包括拦截发生的页面(document-uri),页面的referrer,违反页面策略的资源(blocked-uri),所违反的指令(violated-directive)以及页面所有的内容安全策略(original-policy)。
**现实用法**
CSP现在在Chrome 16+和Firefox 4+的浏览器上可用,并且它在IE10上预计会获得有限的支持。Safari目前还不支持,但是WebKit每晚构建版已经可用,所以预计Safari将会在下面的迭代中提供支持。
下面让我们看一些常用的用例:
**实际案例1:社会化媒体widget**
- Google [+1 button](http://www.google.com/intl/en/webmasters/+1/button/index.html)包括来自[https://apis.google.com](https://apis.google.com/)的脚本,以及嵌入自https://plusone.google.com的iframe。你的策略需要包含这些源来使用Google +1的按钮。最简单的策略是script-src [https://apis.google.com](https://apis.google.com/); frame-src https://plusone.google.com。你还需要确保Google提供的JS片段存放在外部的JS文件里。
- Facebook的[Like按钮](http://developers.facebook.com/docs/reference/plugins/like/)有许多种实现方案。我建议你坚持使用iframe版本,因为它可以和你站点的其它部分保持很好的隔离。这需要使用frame-src [https://facebook.com](https://facebook.com/)指令。请注意,默认情况下,Facebook提供的iframe代码使用的是相对路径//facebook.com,请把这段代码修改为[https://facebook.com](https://facebook.com/),HTTP你没有必要可以不使用。
- Twitter的[Tweet按钮](https://twitter.com/about/resources/buttons)依赖于script和frame,都来自于[https://platform.twitter.com(Twitter](https://platform.twitter.com%28twitter/)默认提供的是相对URL,请在复制的时候编辑代码来指定为HTTPS方式)。
其它的平台有相似的情况,可以类似的解决。我建议把default-src设置为none,然后查看控制台来检查你需要使用哪些资源来确保widget正常工作。
使用多个widget非常简单:只需要合并所有的策略指令,记住把同一指令的设置都放在一起。如果你想使用上面这三个widget,策略看起来会像下面这样:
~~~
script-src https://apis.google.com https://platform.twitter.com; frame-src https://plusone.google.com https://facebook.com https://platform.twitter.com
~~~
**实际案例2:防御**
假设你访问一个银行网站,并且希望确保只加载你所需的资源。在这种情况下,开始设置一个默认的权限来阻止所有的内容(default-src ‘none’),并且从这从头构建策略。
比如,银行网站需要从来自[https://cdn.mybank.net](https://cdn.mybank.net/)的CDN加载图像、样式和脚本,并且通过XHR连接到[https://api.mybank.com/](https://api.mybank.com/)来拉取各种数据,还需要使用frame,但是frame都来自非第三方的本地页面。网站上没有Flash、字体和其他内容。这种情况下我们可以发送最严格的CSP头是:
~~~
Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; frame-src 'self'
~~~
**实际案例3:只用SSL**
一个婚戒论坛管理员希望所有的资源都通过安全的方式进行加载,但是不想真的编写太多代码;重写大量第三方论坛内联脚本和样式的代码超出了他的能力。所以以下的策略将会是非常有用的:
~~~
Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'
~~~
尽管default-src指定了https,脚本和样式不会自动继承。每个指令将会完全覆盖默认资源类型。
**未来**
W3C的[Web应用安全工作组](http://www.w3.org/2011/webappsec/)正在制定内容安全策略规范的细节,1.0版本将要进入最后修订阶段,它和本文描述的内容已经非常接近。而public-webappsec@邮件组正在讨论1.1版本,浏览器厂商也在努力巩固和改进CSP的实现。
CSP 1.1在画板上有一些有趣的地方,值得单独列出来:
**通过meta标签添加策略**:CSP的首选设置方式是HTTP头,它非常有用,但是通过标记或者脚本设置会更加直接,不过目前还未最终确定。WebKit已经实现了[通过meta元素进行权限设置](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#html-meta-element--experimenta)的特性,所以你现在可以在Chrome下尝试如下的设置:在文档头添加<metahttp-equiv="X-WebKit-CSP" content="[POLICY GOES HERE]">。
你甚至可以在运行时通过脚本来添加策略。
**DOM API:**如果CSP的下一个迭代添加了这个特性,你可以通过Javascript来查询页面当前的安全策略,并根据不同的情况进行调整。例如在eval()是否可用的情况下,你的代码实现可能会有些许不同。这对JS框架的作者来说非常有用;并且API规范目前还非常不确定,你可以在[规范草案的脚本接口章节](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#script-interfaces)找到最新的迭代版本。**
**新的指令**:许多新指令正在讨论中,包括[script-nonce](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#script-nonce--experimental):只有明确指定的脚本元素才能使用内联脚本;[plugin-types](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#plugin-types--experimental):这将限制插件的类型;[form-action](https://dvcs.w3.org/hg/content-security-policy/raw-file/tip/csp-specification.dev.html#form-action--experimental):允许form只能提交到特定的来源。
如果你对这些未来特性的讨论感兴趣,可以阅读[邮件列表的归档](http://lists.w3.org/Archives/Public/public-webappsec/)或者加入邮件列表。
本文译自:[http://www.html5rocks.com/en/tutorials/security/content-security-policy/](http://www.html5rocks.com/en/tutorials/security/content-security-policy/)
转载请注明:[来自蒋宇捷的博客](http://blog.csdn.net/hfahe)
- 前言
- AutoPager的简单实现
- 利用CSS3特性巧妙实现漂亮的DIV箭头
- IE9在Win7下任务栏新特性简介
- 浏览器九宫格的简单实现
- Raphael js库简介
- 使用CSS3构建Ajax加载动画
- 用CSS3创建动画价格表
- 用CSS3实现浏览器的缩放功能
- 用纯CSS3实现QQ LOGO
- 用CSS3创建旋转载入器
- 使用Javascript开发移动应用程序
- 用HTML5创建超酷图像灰度渐变效果
- 使用CSS3创建文字颜色渐变(CSS3 Text Gradient)
- 仅用CSS创建立体旋转幻灯片
- 如何创建跨浏览器的HTML5表单
- 用CSS3实现动画进度条
- HTML5 Guitar Tab Player
- 奇妙的HTML5 Canvas动画实例
- 谈HTML5和CSS3的国际化支持
- 实现跨浏览器的HTML5占位符
- 前端开发必备工具:WhatFont Bookmarklet-方便的查询网页上的字体
- 使用HTML5和CSS3来创建幻灯片
- HTML5之美
- 如何使用HTML5创建在线精美简历
- 以小见大、由浅入深-谈如何面试Javascript工程师
- 快速入门:HTML5强大的Details元素
- 用CSS3实现图像风格
- HTML5视频字幕与WebVTT
- 用纯CSS3实现Path华丽动画
- 用3个步骤实现响应式网页设计
- 遇见CSS3滤镜
- 关于CSS3滤镜的碎念
- 用纯CSS3绘制萌系漫画人物动态头像
- CSS3新的鼠标样式介绍
- 用HTML5献上爱的3D玫瑰
- 对HTML5 Device API相关规范的解惑
- 如何使用HTML5实现拍照上传应用
- 2012第一季度国外HTML5移动开发趋势
- HTML5新特性:范围样式
- 百度开发者大会-《用HTML5新特性开发移动App》PPT分享
- Chrome 19对于HTML5最新支持的动态:电池状态API,全屏API,震动API,语音API
- 遇见Javascript类型数组(Typed Array)
- 用HTML5 Audio API开发游戏音乐
- 用HTML5实现人脸识别
- 用Javascript实现人脸美容
- Chrome 20对于HTML5最新支持的动态:颜色输入,网络信息API,CSS着色器
- 用HTML5实现手机摇一摇的功能
- 用HTML5实现iPad应用无限平滑滚动
- 用非响应式设计构建跨端Web App
- 了解SVG
- HTML5图像适配介绍
- HTML5安全:内容安全策略(CSP)简介
- HTML5安全:CORS(跨域资源共享)简介
- 用CSS3 Region和3D变换实现书籍翻页效果
- 谈谈移动App的思维误区
- Chrome新特性:文件夹拖拽支持
- 《关注HTML5安全》
- HTML5安全风险详析之一:CORS攻击
- HTML5安全风险详析之二:Web Storage攻击
- HTML5图像适配最新进展:响应式图片规范草案
- HTML5移动Web App相关标准状态及路线图
- HTML5安全风险详析之三:WebSQL攻击
- Chrome引入WebRTC支持视频聊天App
- HTML5安全风险详析之四:Web Worker攻击
- HTML5安全风险详析之五:劫持攻击
- HTML5安全风险详析之六:API攻击
- HTML5安全攻防详析之七:新标签攻击
- 在iOS Safari中播放离线音频
- 使用WebRTC实现远程屏幕共享
- Firefox、Android、iOS遇见WebRTC
- HTML5光线传感器简介
- HTML5安全攻防详析之八:Web Socket攻击
- HTML5安全攻防详析之完结篇:HTML5对安全的改进
- 激动人心!在网页上通过语音输入文字 - HTML5 Web Speech API介绍
- Web滚动性能优化实战
- 用CSS3设计响应式导航菜单
- 用HTML5构建高性能视差网站
- 漫谈@supports与CSS3条件规则
- HTML5下载属性简介
- 如何开发优秀的HTML5游戏?-迪斯尼《寻找奥兹之路》游戏技术详解(一)
- 如何开发优秀的HTML5游戏?-迪斯尼《寻找奥兹之路》游戏技术详解(二)
- 趋势:Chrome为打包应用提供强大新特性
- 从HTML5移动应用现状谈发展趋势
- 基于HTML5的Web跨设备超声波通信方案