为什么谷歌会在(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。