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?
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 awhile(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.It prevents JSON hijacking, a major JSON security issue that is formally fixed in all major browsers since 2011 with EMCA5.
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 atmail.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.
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:
This is however valid JSON:
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.
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
andArray
constructors. Those can be altered such that otherwise innocuous JSON, when parsed, could trigger attacker code.Since the
<script>
tag is exempted from the Same Origin Policy which is a security necessity in the web world,while(1)
when added to the JSON response prevents misuse of it in the<script>
tag.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.