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?
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.
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:
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.
Similarly, adding invalid tokens before the JSON, like &&&START&&&
, makes sure that it is never executed.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With