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

这是怎么回事?


这是为了确保某些其他网站不会使用卑鄙的手段来窃取您的数据。例如,通过替换数组构造函数,然后通过<script>标记包含此JSON URL,恶意第三方站点可以从JSON响应中窃取数据。通过放置一段时间(1);在开始时,脚本将挂起。

另一方面,使用XHR和单独的JSON解析器的同一站点请求可以很容易地忽略while(1);前缀


注意:截至2019年,导致本问题中讨论的预防措施的许多旧漏洞在现代浏览器中不再是问题。我会把下面的答案作为一个历史性的好奇,但事实上,自2010年(!!)被问及这个问题以来,整个话题已经发生了根本性的变化。


它防止它被用作简单<script>标记的目标。(好吧,这并不能阻止它,但它会让它变得不愉快。)这样,坏人就不能只将脚本标签放在自己的网站上,并依靠活动会话来获取你的内容。

编辑-记录评论(和其他答案)。问题与被破坏的内置设施有关,特别是Object和Array构造函数。可以对这些进行修改,以便在解析无害的JSON时触发攻击者代码。


这将使第三方很难将JSON响应插入带有<script>标记的HTML文档中。请记住,<script>标记不受同源策略的约束。


它防止了JSON劫持,这是一个主要的JSON安全问题,自2011年以来,所有主要浏览器都通过ECMAScript 5正式解决了这个问题。

有争议的例子:假设谷歌有一个类似mail.Google.com/json的URL?action=inbox,以JSON格式返回收件箱的前50条消息。由于同源策略,其他域上的恶意网站无法发出AJAX请求来获取这些数据,但它们可以通过<script>标签包含URL。通过cookie访问URL,并且通过重写全局数组构造函数或访问器方法,每当设置对象(数组或哈希)属性时,都可以调用一个方法,从而允许它们读取JSON内容。

while(1);或者&&&BLAH&&&阻止了这一点:mail.google.com上的AJAX请求将完全访问文本内容,并可以将其删除。但是<script>标记插入会盲目地执行JavaScript而不进行任何处理,从而导致无限循环或语法错误。

这并不能解决跨站点请求伪造的问题。


它防止通过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。


由于<script>标记不受同源策略的约束,这是web世界中的一个安全必要条件,而(1)当添加到JSON响应时,可以防止在<script>标签中误用它。


身份验证到位后,JSON劫持保护可能需要各种形式。Google将while(1)附加到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劫持转移到更富有成效的数据耕作技术,我目前不会讨论)