它可以防止通过 JSON 劫持泄露响应。
理论上,HTTP 响应的内容受同源策略保护:来自一个域的页面无法从另一个域的页面获取任何信息(除非明确允许)。
攻击者可以代表您请求其他域上的页面,例如通过使用<script src=...>
或<img>
标记,但它无法获得有关结果的任何信息(标题、内容)。
因此,如果您访问攻击者的页面,它就无法从 gmail.com 读取您的电子邮件。
除了使用脚本标签请求 JSON 内容时,JSON 在攻击者的受控环境中作为 JavaScript 执行。如果攻击者可以替换对象构造过程中使用的数组或对象构造函数或其他一些方法,那么 JSON 中的任何内容都将通过攻击者的代码并被披露。
请注意,这发生在 JSON 作为 JavaScript 执行时,而不是在解析时发生。
有多种对策:
确保 JSON 永远不会执行
通过while(1);
在 JSON 数据之前放置一条语句,Google 可确保 JSON 数据永远不会作为 JavaScript 执行。
只有合法的页面才能真正获取整个内容,剥离while(1);
,并将剩余部分解析为 JSON。
for(;;);
例如,在 Facebook 上已经看到了类似的事情,结果相同。
确保 JSON 不是有效的 JavaScript
同样,在 JSON 之前添加无效令牌,例如&&&START&&&
,确保它永远不会被执行。
始终返回带有外部对象的 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,知道对象为空本身可能是有value的信息。
以上方法的比较
OWASP 方式的侵入性较小,因为它不需要更改客户端库,并传输有效的 JSON。然而,不确定过去或未来的浏览器错误是否可以解决这个问题。正如@oriadam 所指出的,目前尚不清楚数据是否会通过错误处理(例如 window.onerror)在解析错误中泄漏。
谷歌的方式需要一个客户端库才能支持自动反序列化,并且可以认为在浏览器错误方面更安全。
这两种方法都需要服务器端更改,以避免开发人员意外发送易受攻击的 JSON。