为什么谷歌会在(1);他们的(私有)JSON响应?
例如,在Google calendar中打开和关闭日历时有一个响应:
while (1);
[
['u', [
['smsSentFlag', 'false'],
['hideInvitations', 'false'],
['remindOnRespondedEventsOnly', 'true'],
['hideInvitations_remindOnRespondedEventsOnly', 'false_true'],
['Calendar ID stripped for privacy', 'false'],
['smsVerifiedFlag', 'true']
]]
]
我认为这是为了防止人们对它进行求值(),但你真正需要做的就是替换while,然后就可以设置了。我假设eval预防是为了确保人们编写安全的JSON解析代码。
我也在其他几个地方看到过这种用法,但在谷歌(邮件、日历、联系人等)中更是如此。奇怪的是,谷歌文档以&&&START&&开头,而谷歌联系人似乎以while开头(1)&&&开始&&&。
这是怎么回事?
它防止通过JSON劫持泄露响应。
理论上,HTTP响应的内容受到同源策略的保护:来自一个域的页面无法从另一个域上的页面获取任何信息(除非明确允许)。
攻击者可以代表您请求其他域上的页面,例如使用<script src=…>或<img>标记,但它无法获得有关结果的任何信息(标题、内容)。
因此,如果您访问攻击者的页面,它将无法从gmail.com读取您的电子邮件。
除了使用脚本标记请求JSON内容时,JSON在攻击者控制的环境中作为JavaScript执行。如果攻击者可以替换Array或Object构造函数或对象构建过程中使用的其他方法,JSON中的任何内容都会通过攻击者的代码,并被泄露。
注意,当JSON作为JavaScript执行时会发生这种情况,而不是在解析时。
有多种应对措施:
确保JSON永远不会执行
通过放置一段时间(1);语句之前,Google确保JSON数据永远不会作为JavaScript执行。
只有合法的页面才能真正获得全部内容,去掉while(1);,并将剩余部分解析为JSON。
类似于(;;);例如,在Facebook上也看到了同样的结果。
确保JSON不是有效的JavaScript
类似地,在JSON之前添加无效的标记,如&&&START&&,可以确保它永远不会被执行。
始终返回外部带有Object的JSON
这是OWASP推荐的防止JSON劫持的方法,也是侵入性较小的方法。
与前面的对策类似,它确保JSON永远不会作为JavaScript执行。
一个有效的JSON对象,如果没有被任何东西括起来,在JavaScript中是无效的,因为{}被解释为一个代码块:
eval('{"foo":"bar"}')
// SyntaxError: Unexpected token :
然而,这是有效的JSON:
JSON.parse('{"foo":"bar"}')
// Object {foo: "bar"}
因此,确保始终在响应的顶层返回Object,并确保JSON不是有效的JavaScript,同时仍然是有效的JSON。
正如@hvd在评论中指出的,空对象{}是有效的JavaScript,知道对象是空的本身可能是有价值的信息。
上述方法的比较
OWASP方式侵入性较小,因为它不需要更改客户端库,并且传输有效的JSON。然而,不确定过去或未来的浏览器错误是否能战胜这一点。正如@oriadam所指出的,目前尚不清楚数据是否会通过错误处理在解析错误中泄漏(例如window.onerror)。
谷歌的方式需要一个客户端库,以支持自动反序列化,并且可以认为在浏览器bug方面更安全。
这两种方法都需要服务器端更改,以避免开发人员意外发送易受攻击的JSON。
由于这是一个高流量的帖子,我希望在这里提供一个比原始问题稍不确定的答案,从而提供JSON劫持攻击及其后果的进一步背景
顾名思义,JSON劫持是一种类似于跨站点请求伪造的攻击,攻击者可以从将敏感数据作为数组文本返回给GET请求的应用程序访问跨域敏感JSON数据。JSON调用返回数组文本的示例如下所示:
[{"id":"1001","ccnum":"4111111111111111","balance":"2345.15"},
{"id":"1002","ccnum":"5555555555554444","balance":"10345.00"},
{"id":"1003","ccnum":"5105105105105100","balance":"6250.50"}]
这种攻击可以通过三个主要步骤实现:
步骤1:让经过身份验证的用户访问恶意页面。步骤2:恶意页面将尝试从用户登录的应用程序访问敏感数据。这可以通过在HTML页面中嵌入脚本标记来实现,因为同源策略不适用于脚本标记。
<script src="http://<jsonsite>/json_server.php"></script>
浏览器将向json_server.php发出GET请求,用户的任何身份验证cookie都将随请求一起发送。步骤3:此时,当恶意站点执行脚本时,它无权访问任何敏感数据。通过使用对象原型设置器可以访问数据。在下面的代码中,当试图设置“ccnum”属性时,对象原型属性被绑定到定义的函数。
Object.prototype.__defineSetter__('ccnum',function(obj){
secrets =secrets.concat(" ", obj);
});
此时,恶意站点已成功劫持了json_server.php返回的敏感财务数据(ccnum)JSON文件
需要注意的是,并非所有浏览器都支持这种方法;概念验证是在Firefox 3.x上完成的。该方法现在已被弃用,并被useObject.defineProperty取代。该攻击还有一种变体,适用于所有返回全名JavaScript(例如pi=3.14159)而不是JSON数组的浏览器。
有几种方法可以防止JSON劫持:
由于SCRIPT标记只能生成HTTP GET请求,因此它们只能向POST返回JSON对象请求。阻止web浏览器将JSON对象解释为有效的JavaScript代码。通过要求所有JSON请求都需要预定义的随机值来实现跨站点请求伪造保护。
如您所见,While(1)位于最后一个选项之下。最简单的说法是,while(1)是一个无限循环,它将一直运行到显式发出break语句。因此,将被描述为应用密钥的锁(google break语句)。因此,黑客没有密钥的JSON劫持将始终被驳回。唉,如果使用解析器读取JSON块,while(1)循环将被忽略。
因此,总而言之,while(1)循环可以更容易地可视化为一个简单的break语句密码,谷歌可以使用它来控制数据流。
然而,该语句中的关键字是单词“simple”。值得庆幸的是,自2010年以来,经过验证的无限循环的使用已从基本实践中删除,因为它在隔离时CPU使用率绝对下降(事实上,互联网已不再强制执行粗糙的“快速修复”)。如今,代码库已经嵌入了预防措施,系统不再是关键或有效的。(其中一部分是从JSON劫持转移到更富有成效的数据耕作技术,我目前不会讨论)