JSONP全称是JSON with Padding,是基于JSON格式的为办理跨域要求资源而产生的办理方案。它的基本事理是利用HTML的元素标签,远程调用JSON文件来实现数据通报。如果想在a.com域下获取b.com下的JSON数据(getUsers.JSON):
那么可以首先通过JSONP的“Padding”这个getUsers.JSON输出为:
对付实际运用过程中callback的名称,后台实现是动态输出的。上面例子用PHP实现如下:
然后在a.com利用<script>进行远程调用,在jQuery中可以直接这样调用:
然而,安全问题一贯伴随着业务发展,JSONP同样带来各种安全问题。本文就将梳理JSONP实现过程中的安全攻防。
JSON挟制
JSON挟制又称“JSON Hijacking”,2008年国外安全研究职员开始提到由JSONP带来的风险。这个问题属于CSRF(Cross-site request forgery跨站要求假造)攻击范畴,当某网站通过JSONP的办法跨域(一样平常为子域)通报用户认证后的敏感信息时,攻击者可以布局恶意的JSONP调用页面,勾引被攻击者访问,以达到截取用户敏感信息的目的。一个范例的JSON Hijacking攻击代码如下:
这是乌云上报告的一个攻击案例(WooYun-2012-11284),当被攻击者登录360网站并访问该网页时,个人隐私数据(如用户名、邮箱等)可能被攻击者挟制。
虽然这种攻击已涌现多年,但目前在大的门户网站还普遍存在,而且由于安全意识薄弱,许多企业并未意识到这一问题的主要性。
不过许多甲方公司开始重视此类安全问题,动手研究办理方案。方案之一便是验证JSON文件调用的来源(Referer)。它紧张利用<script>远程加载JSON文件时会发送Referer的机制,在网站输出JSON数据时,判断Referer是否包含在白名单内。这个方法理论上可行,但详细实现过程随意马虎涌现两种逻辑问题。
【Referer过滤(正则)不严谨】
【空Referer】
在很多情形下,开拓者支配过滤Referer来源时,忽略了空Referer的过滤。一样平常情形下浏览器直接访问某URL是不带Referer的,因此很多防御支配许可空Referer。正好由于这个轻忽,导致了全体防御系统奔溃,由于在通过跨协议调用JavaScript时,发送的HTTP要求中Referer为空。跨协议调用的一个大略例子如下:
代码中我们利用<iframe>调用JavaScript伪协议来实现空Referer调用JSON文件。
其余一种手段是通过随机token防御,这项技能在腾讯的网站上运用较多,例如通过http://r.qzone.qq.com/cgi-bin/tfriend/friend_show_qqfriends.cgi?uin=[QQ号]&g_tk=[随机token]输出JSON。这个方案是有效的,但同样存在防御实现不严谨的问题。例如此token可通过以下办法暴力破解:
当然,这些都是纯挚针对“JSON挟制”本身展开的攻防战。在现实中,许多漏洞是相互合营实现打破的。例如上面提到的限定Referer+支配随机token实现都很完美,理论上无懈可击,但只要该网站存在XSS漏洞,就可能让你的防御体系瞬间崩溃!
这里顺带一提,以上是一些通用实现“JSON挟制”的方法,但现实中,某些浏览器的一些特有处理机制(如CSS加载、缺点信息显示等),也能引发类似“JSON挟制”(攻击工具不一定是JSON)的攻击。
callback可定义导致的安全问题
为了方便前端开拓调用,输出一样平常都是可定义的,前文提到的PHP实现的代码:
便是由于可定义callback名输出点,导致了各种安全问题。当然严格来说,里面提到的详细数据输出也是可以利用的,只是本文重点强调callback这个输出点。
【Content-Type与XSS漏洞】
在JSON刚涌现时,大多数开拓者还没有良好的编码习气。输出JSON时,没有严格定义Content-Type(Content-Type: application/json),再加上callback输出点没有进行过滤,直接导致了一个范例的XSS漏洞,上文演示的getUsers.php就存在这个问题:
对Content-Type来说,早期还有一部分人比较喜好利用application/javascript,而这个头在IE等浏览器下一样可以解析HTML导致XSS漏洞。对付这种类型的漏洞,防御紧张从以下两点支配。
a. 严格定义Content-Type: application/json
这样的防御机制导致了浏览器不解析恶意插入的XSS代码(直接访问提示文件下载)。但凡事都有例外,在IE的进化过程中就曾涌现通过一些技巧,可以绕过Content-Type防御解析HTML的事宜,例如在IE6、7等版本要求的URL文件后加一个/x.html就可以解析HTML(http://127.0.0.1/getUsers.php/x.html?callback=<script>alert(/xss/)</script>),详细可参考http://hi.baidu.com/hi_heige/item/f1ecde01c4af3ed61ef04646。
b. 过滤callback以及JSON数据输出
这种机制是比较传统的攻防思维,对输出点进行XSS过滤——又是一个看上去很完美的办理方案,但每每都“事与愿违”。2011年,一个utf7-BOM就复活了n个XSS漏洞。这种攻击办法紧张存在于IE中(新版IE已修复),当我们在callback点输出+/v8这样的utf7-BOM时,IE浏览器会把当前实行的编码认为是utf7,因此我们通过utf7提交的XSS代码会被自动解码并实行。例如:
个中:
URLdecode为:
+/v8为utf7-BOM,其后为我们注入通过utf-7编码后的XSS代码:
<htm><body><script>alert(1);
</script></body></htm>。
利用utf7-BOM是一种非常有代表性的通用方法,除了升级IE进行防御,开拓者也可直接指定Content-Type的编码(Content-Type: application/json; charset=utf-8)。然而只管如此,仍有绕过这些防御方法的可能。
上文提到的a、b两种防御缺一都可能涌现问题,那么我们利用“a+b方案”是否就万无一失了呢?统统皆有可能,我们拭目以待。
其他文件格式(Content-Type)与JSON
【MHTML与JSONP】
2011年,IE曾涌现一个通过MHTML协议解析跨域的漏洞:MHTML Mime-Formatted Request Vulnerability(CVE-2011-0096,https://technet.microsoft.com/library/security/ms11-026)。而当时的一种常见攻击办法便是利用JSONP调用机制中的callback函数名输出点:
它充分利用了callback输出点直接输出MHTML文件格式,然后利用<iframe>调用MHTML标签解析并实行HTML及JavaScript代码,这也是一个通用性的XSS漏洞(UXSS),随后微软紧急推出理解决方案及漏洞补丁程序。在微软推出安全补丁前,这个漏洞已影响Google等大型网站,当时Google为了防御这类攻击启用的方法是,在JSON输出callback时,在文件开头增加了多个换行符,让远程MHTML调用时解析失落败。
在攻击角度来说,它充分利用了打算机体系的各种文件格式识别机制。在这个思维的勾引下,之后还涌现过多次由文件格式加载带来的安全问题,例如CSS文件格式加载导致的类“JSON挟制”,JavaScript加载及各种文件格式编码带来的安全问题等。历史进程每每会涌现各种惊人的相似,JSONP与文件格式的传奇还在上演。
【FLASH与JSONP】
该来的究竟会来,只是没想到相似的场景上演得这么快。就在最近的一次Flash安全更新(security bulletin APSB14-17)中修复了一个安全漏洞:
These updates include additional validation checks to ensure that Flash Player rejects malicious content from vulnerable JSONP callback APIs (CVE-2014-4671).
这个漏洞因影响了Google、Facebook、Tumblr等大网站而备受媒体关注。其攻击技能与JSONP的callback点息息相关。这个问题紧张发生在HTML通过<embed>、<object>调用远程Flash文件时,会直接忽略Content-Type,而JSONP的callback输出一样平常都在文件开头,那么完备可以通过callback点输出一个swf的文件,然后远程HTML调用并运行swf文件。例如:
这是早在2012年就提出的通过callback输出的swf文件流,其实际效果是在被攻击的网站上存放了一个恶意swf文件,HTML远程调用这个swf文件,可直接导致CSRF攻击。
细心的朋友可能创造,上面代码callback输出的swf文件流中存在各种分外字符,通过前文提到的“b. 过滤callback以及JSON数据输出”方案可以直接拦截,对付Goolge、Facebook这样久经磨练的大网站来说,防御不在话下。
在Flash更新“security bulletin APSB14-17”发布后,该漏洞创造者给出了漏洞细节,个中一个亮点,便是作者实现了一个纯alphanumeric输出swf文件的方法:
因此,对付纯alphanumeric输出来说,那些针对XSS的过滤显然可以直接忽略,这个漏洞也证明前文我们提到的“a+b方案”能被直接绕过。
防御
通过上面的攻防对抗演习训练,很多开拓者可能会觉得有点悲剧,各种防御机制彷佛都有办法绕过。这里我想到一个真理:没有绝对的安全。那么我们防御的意义在哪里呢?我认为防御的意义便是虽然没办法让程序最安全(绝对安全),但可以让它更安全。提高攻击者的技能本钱和门槛是安全防御的一个紧张并主要的思路。回到详细的JSONP防御上,可以总结如下几点。
严格安全地实现CSRF办法调用JSON文件:限定Referer、支配一次性token等。严格安装JSON格式标准输出Content-Type及编码(Content-Type: application/json; charset=utf-8)。严格过滤callback函数名及JSON数据的输出。严格限定对JSONP输出callback函数名的长度(例如防御上面Flash输出的方法)。其他一些比较“猥琐”的方法:例如在callback输出前加入其他字符(如//、换行符等)这样不影响JSON文件加载,又能一定程度预防其他文件格式的输出。Gmail就曾利用AJAX的办法获取JSON,通过在输出JSON之前加入while(1);这样的代码防止JavaScript远程调用。作者简介:周景平,知道创宇首席安全官,曾是一名拥有5年多从业履历的外科年夜夫,2012年加入知道创宇,转行安全。网络ID:Superhei(黑哥)
本文选自程序员电子版2014年8月刊,该期更多文章请查看这里。2000年创刊至今所有文章目录请查见地式员封面秀。欢迎订阅程序员电子版(含iPad版、Android版、PDF版)。