HTML5安全介绍之内容安全策略(CSP)简介


Posted in HTML / CSS onJuly 10, 2012

 万维网的安全策略植根于同源策略。例如3water.com的代码只能访问3water.com的数据,而没有访问http://www.baidu.com的权限。每个来源都与网络的其它部分分隔开,为开发人员构建了一个安全的沙箱。理论上这是完美的,但是现在攻击者已经找到了聪明的方式来破坏这个系统。
        这就是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:script-src 'self' https://apis.google.com
        是不是很简单?script-src可以为指定页面控制脚本相关权限。这样浏览器只会下载和执行来自http://apis.google.com和本页自身的脚本。
        一旦我们定义了这个策略,浏览器会在检测到注入代码时抛出一个错误(请注意是什么浏览器)。
内容安全策略适用于所有常用资源
        虽然脚本资源是最明显的安全隐患,但是CSP还提供了一套丰富的指令集,允许页面控制加载各种类型的资源,例如如下的类型:
content-src:限制连接的类型(例如XHR、WebSockets和EventSource)
font-src:控制网络字体的来源。例如可以通过font-src 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;script-src https://host2.com的形式,第二个指令将会被忽略。正确的写法是script-src https://host1.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域名,443端口)。
        你在来源列表中还可以使用四个关键字:
“none”:你可能期望不匹配任何内容
“self”:与当前来源相同,但不包含子域
“unsafe-inline”:允许内联Javascript和CSS
“unsafe-eval”:允许文本到JS的机制例如eval
        请注意,这些关键词需要加引号。
沙箱
        这里还有另外一个值得讨论的指令:sandbox。和其他指令有些不一致,它主要是控制页面上采取的行为,而不是页面能够加载的资源。如果设置了这个属性,页面就表现为一个设置了sandbox属性的frame一样。这对页面有很大范围的影响,例如防止表单提交等。这有点超出了本文的范围,但是你可以在HTML5规范的“沙箱标志设置”章节找到更多信息。
有害的内联代码
        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标签都需要提取到外部样式表中。这样可以防止各式各样神奇的数据泄漏方式。
        如果你必须要有内联脚本和样式,可以为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包括来自https://apis.google.com的脚本,以及嵌入自https://plusone.google.com的iframe。你的策略需要包含这些源来使用Google +1的按钮。最简单的策略是script-src https://apis.google.com; frame-src https://plusone.google.com。你还需要确保Google提供的JS片段存放在外部的JS文件里。
Facebook的Like按钮有许多种实现方案。我建议你坚持使用iframe版本,因为它可以和你站点的其它部分保持很好的隔离。这需要使用frame-src https://facebook.com指令。请注意,默认情况下,Facebook提供的iframe代码使用的是相对路径//facebook.com,请把这段代码修改为https://facebook.com,HTTP你没有必要可以不使用。
Twitter的Tweet按钮依赖于script和frame,都来自于https://platform.twitter.com(Twitter默认提供的是相对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的CDN加载图像、样式和脚本,并且通过XHR连接到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应用安全工作组正在制定内容安全策略规范的细节,1.0版本将要进入最后修订阶段,它和本文描述的内容已经非常接近。而public-webappsec@邮件组正在讨论1.1版本,浏览器厂商也在努力巩固和改进CSP的实现。
        CSP 1.1在画板上有一些有趣的地方,值得单独列出来:
        通过meta标签添加策略:CSP的首选设置方式是HTTP头,它非常有用,但是通过标记或者脚本设置会更加直接,不过目前还未最终确定。WebKit已经实现了通过meta元素进行权限设置的特性,所以你现在可以在Chrome下尝试如下的设置:在文档头添加<metahttp-equiv="X-WebKit-CSP" content="[POLICY GOES HERE]">。
        你甚至可以在运行时通过脚本来添加策略。
        DOM API:如果CSP的下一个迭代添加了这个特性,你可以通过Javascript来查询页面当前的安全策略,并根据不同的情况进行调整。例如在eval()是否可用的情况下,你的代码实现可能会有些许不同。这对JS框架的作者来说非常有用;并且API规范目前还非常不确定,你可以在规范草案的脚本接口章节找到最新的迭代版本。
        新的指令:许多新指令正在讨论中,包括script-nonce:只有明确指定的脚本元素才能使用内联脚本;plugin-types:这将限制插件的类型;form-action:允许form只能提交到特定的来源。
        如果你对这些未来特性的讨论感兴趣,可以阅读邮件列表的归档或者加入邮件列表。
        本文译自:http://www.html5rocks.com/en/tutorials/security/content-security-policy/
摘自:蒋宇捷的博客

HTML / CSS 相关文章推荐
IE下实现类似CSS3 text-shadow文字阴影的几种方法
May 11 HTML / CSS
纯CSS3实现自定义Tooltip边框涂鸦风格的教程
Nov 05 HTML / CSS
利用CSS3实现开门效果实例源码
Aug 22 HTML / CSS
使用 css3 实现圆形进度条的示例
Jul 05 HTML / CSS
canvas绘制文本内容自动换行的实现代码
Jan 14 HTML / CSS
Html5新标签解释及用法
Feb 17 HTML / CSS
突袭HTML5之Javascript API扩展3—本地存储全新体验
Jan 31 HTML / CSS
使用纯HTML5编写一款网页上的时钟的代码分享
Nov 16 HTML / CSS
html5 div布局与table布局详解
Nov 16 HTML / CSS
HTML5+CSS3模仿优酷视频截图功能示例
Jan 05 HTML / CSS
解决html5中video标签无法播放mp4问题的办法
May 07 HTML / CSS
html2canvas生成的图片偏移不完整的解决方法
May 19 HTML / CSS
开发人员所需要知道的HTML5性能分析面面观
Jul 05 #HTML / CSS
关于HTML5的22个初级技巧(图文教程)
Jun 21 #HTML / CSS
关于HTML5的安全问题开发人员需要牢记的
Jun 21 #HTML / CSS
HTML5 对各个标签的定义与规定:body的介绍
Jun 21 #HTML / CSS
网易微博Web App用HTML5开发的过程介绍
Jun 13 #HTML / CSS
只要五步 就可以用HTML5/CSS3快速制作便签贴特效(图)
Jun 04 #HTML / CSS
关于HTML5你必须知道的28个新特性,新技巧以及新技术
May 28 #HTML / CSS
You might like
php实现模拟post请求用法实例
2015/07/11 PHP
Yii基于数组和对象的Model查询技巧实例详解
2015/12/28 PHP
yii插入数据库防并发的简单代码
2017/05/27 PHP
PHP数据库操作三:redis用法分析
2017/08/16 PHP
jquery实现文本框鼠标右击无效以及不能输入的代码
2010/11/05 Javascript
关于捕获用户何时点击window.onbeforeunload的取消事件
2011/03/06 Javascript
json数据与字符串的相互转化示例
2013/09/18 Javascript
模拟javascript中的sort排序(简单实例)
2016/08/17 Javascript
详解XMLHttpRequest(二)响应属性、二进制数据、监测上传下载进度
2016/09/14 Javascript
vue.js初学入门教程(1)
2016/11/03 Javascript
jQuery和CSS仿京东仿淘宝列表导航菜单
2017/01/04 Javascript
jQuery动态生成表格及右键菜单功能示例
2017/01/13 Javascript
javascript基础进阶_深入剖析执行环境及作用域链
2017/09/05 Javascript
vue源码学习之Object.defineProperty 对数组监听
2018/05/30 Javascript
小程序获取周围IBeacon设备的方法
2018/10/31 Javascript
微信小程序入口场景的问题集合与相关解决方法
2019/06/26 Javascript
Vue生命周期activated之返回上一页不重新请求数据操作
2020/07/26 Javascript
vue-路由精讲 二级路由和三级路由的作用
2020/08/06 Javascript
[43:24]2018DOTA2亚洲邀请赛3月29日 小组赛A组 LGD VS Liquid
2018/03/30 DOTA
Python字符串格式化输出方法分析
2016/04/13 Python
python numpy实现文件存取的示例代码
2019/05/26 Python
pycharm实现在子类中添加一个父类没有的属性
2020/03/12 Python
django-xadmin根据当前登录用户动态设置表单字段默认值方式
2020/03/13 Python
实例代码讲解Python 线程池
2020/08/24 Python
python 发送邮件的四种方法汇总
2020/12/02 Python
用python 绘制茎叶图和复合饼图
2021/02/26 Python
CSS3的transition和animation的用法实例介绍
2014/08/20 HTML / CSS
学生党支部先进事迹
2014/02/04 职场文书
法制宣传月活动总结
2014/04/29 职场文书
采购部长岗位职责
2014/06/13 职场文书
锦旗标语大全
2014/06/23 职场文书
2014年节能工作总结
2014/12/18 职场文书
小学科学教学计划
2015/01/21 职场文书
辛德勒的名单观后感
2015/06/03 职场文书
Mongo服务重启异常问题的处理方法
2021/07/01 MongoDB
Mysql案例刨析事务隔离级别
2021/09/25 MySQL