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?

share|improve this question
21  
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. – voyager Apr 19 '10 at 18:04
7  
probably a follow-up question: Why does google prepend )]}' now instead of while(1);? Would the answers be the same? – Gizmo Feb 16 at 18:51
    
Would prevent eval, but not with an infinite loop. – Mardoxx May 6 at 20:27
up vote 3315 down vote accepted

It prevents JSON hijacking.

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.

share|improve this answer
181  
Why doesn't the request to obtain this data require a CSRF-token instead? – Jakub P. Feb 3 '13 at 1:43
181  
Does for(;;); do the same job? I've seen this in facebook's ajax responses. – King Julien Feb 4 '13 at 8:27
133  
@JakubP. Storing and maintaining CSRF-tokens at Google's scale requires a large amount of infrastructure and cost. – abraham Feb 5 '13 at 5:12
86  
@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 '13 at 6:10
61  
It seems to me a better way would be to let the server only send the JSON if the correct header has been set. You can do that in an AJAX call, but not with script tags. That way, the sensitive information never even gets sent, and you don't have to rely on browser-side security. – mcv Dec 30 '13 at 15:06

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.

share|improve this answer
4  
Technically, a "normal" JSON parser should give an error if you have a prefix. – Matthew Crumley May 16 '09 at 3:31
11  
Attackers would just use a plain old <script> element, not an XHR. – Laurence Gonsalves May 16 '09 at 4:22
8  
@Matthew, sure, but you can remove it before passing the data to the JSON parser. You can't do that with a <script> tag – bdonlan Feb 24 '11 at 12:54
7  
Are there any examples of this? Replacing the array constructor is referenced again, but that's a bug long fixed. I don't understand how one would have access to the data received via the script tag. I'd love to see a dummy implementation which works in recent browser. – Dennis G Feb 5 '13 at 13:37
7  
@joeltine, no, it's not. See stackoverflow.com/questions/16289894/… . – user69173 Jun 3 '13 at 19:44

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 informations from pages on an 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 counter measures:

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 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:

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 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 from accidentally sending vulnerable JSON.

share|improve this answer
13  
OWASP recommendation is interesting because of its simplicity. Anyone know a reason Google's way is more secure? – funroll Mar 15 '14 at 1:47
9  
I believe it isn't more secure in any way. Providing OWASP here seems a good enough reason for +1. – vaxquis Apr 12 '14 at 15:54
3  
What browsers in 2014 let you replace Array or Object constructor? – Filipe Giusti Jul 9 '14 at 18:49
10  
It may be worth noting why returning an object literal fails the script tag or eval function. The braces {} can be interpreted as either a block of code or an object literal, and by itself, JavaScript prefers the former. As a block of code it is, of course, invalid. By this logic, I can’t see any foreseeable changes in future browser behaviour. – Manngo Nov 8 '15 at 10:59
7  
Bad code is not enough becaues an attacker can also hijack the script-error hander of the browser (window.onerror) I'm not sure what is the behavior of onerror with syntax errors. I guess Google were unsure as well. – oriadam Dec 6 '15 at 23:06

That would be to make it difficult for a third-party to insert the JSON response into an HTML document with the <script> tag. Remember that the <script> tag is exempt from the Same Origin Policy.

share|improve this answer
16  
This is only half an answer. If it weren't for the trick of overriding the Object and Array constructors, executing a valid JSON response as though it were JavaScript would be totally innocuous in all circumstances. Yes, the while(1); prevents the response from being executed as JavaScript if targeted by a <script> tag, but your answer doesn't explain why that's necessary. – Mark Amery Jan 17 '14 at 9:37
    
but how it will prevent iframes – Ravinder Payal Aug 3 '16 at 16:24

It prevents it from being used as the target of a simple <script> tag. (Well, it doesn't prevent it, but it makes it unpleasant.) That way bad guys can't just put that script tag in their own site and rely on an active session to make it possible to fetch your content.

edit — note the comment (and other answers). The issue has to do with subverted built-in facilities, specifically the Object and Array constructors. Those can be altered such that otherwise innocuous JSON, when parsed, could trigger attacker code.

share|improve this answer
13  
This is only half an answer. If it weren't for the trick of overriding the Object and Array constructors, executing a valid JSON response as though it were JavaScript would be totally innocuous in all circumstances. Yes, the while(1); prevents the response from being executed as JavaScript if targeted by a <script> tag, but your answer doesn't explain why that's necessary. – Mark Amery Jan 17 '14 at 9:39

protected by LarsTech Dec 30 '13 at 3:35

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.