Categories
ajax javascript json security

Why does Google prepend while(1); to their JSON responses?

4358

Why does Google prepend while(1); to their (private) JSON responses?

For example, here’s a response while turning a calendar on and off in Google Calendar:

while (1);
[
  ['u', [
    ['smsSentFlag', 'false'],
    ['hideInvitations', 'false'],
    ['remindOnRespondedEventsOnly', 'true'],
    ['hideInvitations_remindOnRespondedEventsOnly', 'false_true'],
    ['Calendar ID stripped for privacy', 'false'],
    ['smsVerifiedFlag', 'true']
  ]]
]

I would assume this is to prevent people from doing an eval() on it, but all you’d really have to do is replace the while and then you’d be set. I would assume the eval prevention is to make sure people write safe JSON parsing code.

I’ve seen this used in a couple of other places, too, but a lot more so with Google (Mail, Calendar, Contacts, etc.) Strangely enough, Google Docs starts with &&&START&&& instead, and Google Contacts seems to start with while(1); &&&START&&&.

What’s going on here?

5

  • 54

    I believe that your first impression is correct. If you start looking for code and try to trim the input stream depending on the source, you’d reconsider and do it the safe (and because of Google’s actions, easier) way.

    Apr 19, 2010 at 18:04

  • 45

    probably a follow-up question: Why does google prepend )]}' now instead of while(1);? Would the answers be the same?

    – Gizmo

    Feb 16, 2017 at 18:51


  • 7

    Would prevent eval, but not with an infinite loop.

    – Mardoxx

    May 6, 2017 at 20:27


  • 20

    This )]}' may also be to save bytes, like facebook used for(;;); which saves one byte 🙂

    Jul 8, 2017 at 20:55

  • Why isn’t there a HTTP response header which says “this is not Javascript; don’t load it as such”? You’d think the content type would take care of it.

    – Kaz

    Jul 27 at 11:52


4499

It prevents JSON hijacking, a major JSON security issue that is formally fixed in all major browsers since 2011 with ECMAScript 5.

Contrived example: say Google has a URL like mail.google.com/json?action=inbox which returns the first 50 messages of your inbox in JSON format. Evil websites on other domains can’t make AJAX requests to get this data due to the same-origin policy, but they can include the URL via a <script> tag. The URL is visited with your cookies, and by overriding the global array constructor or accessor methods they can have a method called whenever an object (array or hash) attribute is set, allowing them to read the JSON content.

The while(1); or &&&BLAH&&& prevents this: an AJAX request at mail.google.com will have full access to the text content, and can strip it away. But a <script> tag insertion blindly executes the JavaScript without any processing, resulting in either an infinite loop or a syntax error.

This does not address the issue of cross-site request forgery.

7

  • 251

    Why doesn’t the request to obtain this data require a CSRF-token instead?

    – Jakub P.

    Feb 3, 2013 at 1:43

  • 6

    Wouldn’t returning an object containing the array, instead of the array directly, also solve the problem?

    Feb 3, 2013 at 18:26

  • 5

    @PedroFelix No, that wouldn’t solve the problem since the same attacks mentioned in the post could still be performed. Overriding the accessor methods to retrieve the info.

    – Boushley

    Feb 5, 2013 at 2:36

  • 199

    @JakubP. Storing and maintaining CSRF-tokens at Google’s scale requires a large amount of infrastructure and cost.

    – abraham

    Feb 5, 2013 at 5:12

  • 144

    @JakubP. anti-CSRF tokens mess with caching, and require some amount of cryptographic evaluation server-side. At Google scale, that would require a lot of CPU. This sort of offloads it to the client.

    – bluesmoon

    Feb 5, 2013 at 6:10

635

It prevents disclosure of the response through JSON hijacking.

In theory, the content of HTTP responses are protected by the Same Origin Policy: pages from one domain cannot get any pieces of information from pages on the other domain (unless explicitly allowed).

An attacker can request pages on other domains on your behalf, e.g. by using a <script src=...> or <img> tag, but it can’t get any information about the result (headers, contents).

Thus, if you visit an attacker’s page, it couldn’t read your email from gmail.com.

Except that when using a script tag to request JSON content, the JSON is executed as JavaScript in an attacker’s controlled environment. If the attacker can replace the Array or Object constructor or some other method used during object construction, anything in the JSON would pass through the attacker’s code, and be disclosed.

Note that this happens at the time the JSON is executed as JavaScript, not at the time it’s parsed.

There are multiple countermeasures:

Making sure the JSON never executes

By placing a while(1); statement before the JSON data, Google makes sure that the JSON data is never executed as JavaScript.

Only a legitimate page could actually get the whole content, strip the while(1);, and parse the remainder as JSON.

Things like for(;;); have been seen at Facebook for instance, with the same results.

Making sure the JSON is not valid JavaScript

Similarly, adding invalid tokens before the JSON, like &&&START&&&, makes sure that it is never executed.

Always return JSON with an Object on the outside

This is OWASP recommended way to protect from JSON hijacking and is the less intrusive one.

Similarly to the previous counter-measures, it makes sure that the JSON is never executed as JavaScript.

A valid JSON object, when not enclosed by anything, is not valid in JavaScript, since the { } gets interpreted as a code block:

eval('{"foo":"bar"}')
// SyntaxError: Unexpected token :

This is however valid JSON:

JSON.parse('{"foo":"bar"}')
// Object {foo: "bar"}

So, making sure you always return an Object at the top level of the response makes sure that the JSON is not valid JavaScript, while still being valid JSON.

As noted by @hvd in the comments, the empty object {} is valid JavaScript, and knowing the object is empty may itself be valuable information.

Comparison of above methods

The OWASP way is less intrusive, as it needs no client library changes, and transfers valid JSON. It is unsure whether past or future browser bugs could defeat this, however. As noted by @oriadam, it is unclear whether data could be leaked in a parse error through an error handling or not (e.g. window.onerror).

Google’s way requires a client library in order for it to support automatic de-serialization and can be considered to be safer with regard to browser bugs.

Both methods require server side changes in order to avoid developers accidentally sending vulnerable JSON.

4

  • 26

    OWASP recommendation is interesting because of its simplicity. Anyone know a reason Google’s way is more secure?

    – funroll

    Mar 15, 2014 at 1:47

  • 19

    I believe it isn’t more secure in any way. Providing OWASP here seems a good enough reason for +1.

    – user719662

    Apr 12, 2014 at 15:54

  • I supposed if you must use JSONP you could try to use CSRF tokens in some clever (probably insecure) way.

    Aug 29, 2014 at 2:19

  • I think {} is interpreted as an empty block, not an empty object, otherwise {"foo":"bar"} (as the start of a program) would not throw a syntax error. I.e. the interpreter sees the { a the start of a block, followed by the string "foo" followed by the colon : punctuator in a place that it’s not expected. If the opening brace is seen as the start of an object literal, then the colon would be valid.

    – RobG

    Jun 12 at 7:39


387

This is to ensure some other site can’t do nasty tricks to try to steal your data. For example, by replacing the array constructor, then including this JSON URL via a <script> tag, a malicious third-party site could steal the data from the JSON response. By putting a while(1); at the start, the script will hang instead.

A same-site request using XHR and a separate JSON parser, on the other hand, can easily ignore the while(1); prefix.

0