JSON hijacking is an exploit which has not had the publicity it perhaps deserves. It is a real risk to website security; as much as CSRF or XSS.
It’s easier to understand how JSON hijacking works if you have a basic understanding on the differences between JSON and JSONP.
Traditionally, to utilize a JSON response we need to make a request via XHR, retrieve the responseText
, and then parse it using JSON.parse()
(or similar). As XHR requests are restricted to the same origin policy (SOP), this is only relevant if we’re making the request to the same domain we’re on; and because we (usually) control that domain, there is little risk.
JSONP requests (made by inserting <script>
tags) are not restricted by the SOP, but the problem here is that a JSON response normally has no effect; i.e. there’s no problems retrieving a JSON response via this method, but there’s no way to capture or utilize the response;
<script src="http://www.remote-server.com/get-json.php">
{
"user_id": 1234,
}
</script>
However, clever folks discovered that in some browsers, you can cause an effect to happen;
-
You can override the Array constructor to do something with the array
Array = function () { for (var i=0;i<this.length;i++) { alert(this[i]); } }
-
To access object attributes, you can define a setter to capture the setting of that attribute;
Object.prototype.__defineSetter__("user_id", function (value) { alert("user_id is being set to: " + value); });
Bear in mind that neither of these exploits are cross browser. However, this is a real threat that has been used to target the likes of Google and Twitter.
So how exactly does this work?
- An attacker injects the above snippet(s) on a website you visit (a website he owns?)
- When the request for JSON from the 3rd party domain is made, any cookies you have for that domain are sent with the request; if you’ve got a session on that website, the website is non-the-wiser that it’s not actually you requesting that page.
- When the response is returned, the above snippets are executed, and the attacker can manipulate/ steal the response; any personal/ important details contained in there are now his.
Fortunately, there are a number of ways to fix this;
- Rigorously check for the
X-Requested-With
header to check the request came via XHR. - Add breaking code to the JSON response.
- Facebook lead the JSON response with a
for (;;);
(infinite loop). - Google lead the JSON response with a
throw 1;
(throw an error).
- Facebook lead the JSON response with a
Because of these additions, when the browser evaluates the response, the object/ array declarations are never reached. Obviously in your code, you’ll need to strip the leading mush out before you attempt to parse the JSON;
var json = JSON.parse(this.responseText.slice("for (;;;);".length));
or
var json = JSON.parse(this.responseText.slice("throw 1;".length));
For further reading, you may be interested in the following information articles; JSON Hijacking on http://haacked.com, JSON Hijacking on http://thespanner.co.uk.