然而,通过和微软的Scott Hanselman交流,我了解到另外一个方法可能会影响更多的浏览器。在上周的挪威开发者大会上,我做了一个针对Json劫持漏洞的演示。
如果我们不使用JSON发送敏感数据,或者只对报文请求做出响应,那么我们的网站就不存在这个漏洞。
我不喜欢用流程图展示这个过程,我会尽量用图表描述。在第一页截图上,我们可以看到不知情的受害者登陆漏洞网站,漏洞网站返回了一个身份认证的cookie。
我们都可能收到过一些垃圾邮件,邮件中附有链接,发送者声称这有一段搞笑视频。这些大量的垃圾邮件都是一些别有用心的人发的。
但是实际上,这些链接指向的是那些坏家伙自己网站。当我们点击了链接,接下来的两个步骤会迅速进行。第一,我们的浏览器向这些网站发送请求。
[Authorize]publicJsonResultAdminBalances(){ varbalances=new[]{ new{Id=1,Balance=3.14}, new{Id=2,Balance=2.72}, new{Id=3,Balance=1.62} }; returnJson(balances);}
[{“Id”:1,”Balance”:3.14},{“Id”:2,”Balance”:2.72},{“Id”:3,”Balance”:1.62}]
{“Id”:1,”Balance”:3.14}
<script src=”http://example.com/SomeJson”></script>
注*这里我们可以看到使用 Json Object 而不是Json Array返回你的数据,可以在一定程度上预防这种漏洞。
<html>...<body><scripttype="text/javascript">Object.prototype.__defineSetter__('Id',function(obj){alert(obj);});</script><scriptsrc="//example.com/Home/AdminBalances"></script></body></html>
看到了什么?黑客正在改变对象的原型,用_defineSetter_这种特殊方法,覆盖JSON对象(Object)原本应有的默认行为。
就像之前提到的,坏家伙需要使我们在登录漏洞网站后不久并且在会话仍旧有效时,访问他的恶意的网页。通过含有恶意网站链接的邮件进行钓鱼攻击的方式是很典型的。
因为在IE8上_defineSetter_是一个无效的方法,所以在IE8上看不到现象。我在Chrome和Firefox上都试过,都可以。
避免这个漏洞也很简单:或者从不发送JSON数组,或者只访问HTTP POST以获得需要的数据。举个例子:在ASP.NET MVC中,你可以用AcceptVerbsAttribute来实现:
[Authorize][AcceptVerbs(HttpVerbs.Post)]publicJsonResultAdminBalances(){ varbalances=new[]{ new{Id=1,Balance=3.14}, new{Id=2,Balance=2.72}, new{Id=3,Balance=1.62} }; returnJson(balances);}
ASP.NET和WCF JSON服务端实际上在对象中用了“d”属性,包裹了他们的JSON,这我在另一篇文章中讨论过:
必须经过这些属性获得数据,似乎有些奇怪,但这需要通过一个客户端代理来实现来去除“d”属性,,以便不影响最终用户。
在ASP.NET MVC下,绝大多数开发者没有生成客户端代理,而是使用jQuery和其他类似的库,这样一来使用“d”属性的就有些尴尬。
注*其实MVC的方法有点复杂,到这里我们可以看出,JSON劫持漏洞是要以在受豁者的浏览器上执行JSON返回对象为前提的,其实Google使用了一种更加聪明的方法,通过添加“死循环”命令,防止黑客运行这段脚本,可参见这篇文章:为什么谷歌的JSON响应以while(1);开头?
当然,如果我们用SSL提供JSON文本,这个特定的缓存问题将会很容易被解决。
注*这里我们可以看到防范方法这三:不要cache你的ajax请求,不过目前似乎所有的js库默认都是不cache的。
Mozilla Developer Center发表的一篇文章中写道:对象和数组初始化设定项在赋值时不应该调用setters方法。这一点我同意。尽管有评论认为:也许浏览器真的不应该执行脚本。
在我看来,在当前的客户端库下,安全访问JSON的默认方式应该是POST,并且我们应该选择GET,而不是其他方式。您觉得呢?您所了解的其他平台是怎么解决这个问题的呢?我很想听听大家的想法。
联系客服