<< DWR Website Update | Home | JSON is not as safe as people think it is, part 2 >>

JSON is not as safe as people think it is

I saw some discussion recently about using JSON for secured data, and I'm not sure that everyone understands the risks.

I believe that JSON is unsafe for anything but public data unless you are using unpredictable URLs.

There are 2 problems. CSRF (Cross Site Request Fogery) allows attackers to bypass cookie based authentication. I blogged about it a while ago. Wikipedia talks about it. CSRF allows you to invoke cookie protected actions on a remote server. It allows Mr. Evil to trick Mrs. Innocent into transferring money from her bank account into his.

Far less known perhaps, is the JSON/Array hack that allows a user to steal JSON data on Mozilla and any other platform with a modern JavaScript interpreter.

Update: It's not just Arrays - it you can steal data from objects too.

There are many ways to fetch data from a server, but the interesting cases here are: XHR, iframe and script-tags. Without knowledge of the JSON/Array hack it's easy to reason like this:

  • XHR: Browser cross-domain rules prevent the attacker from making the request in the first place.
  • iframe: The attacker can embed an iframe that points at some remote server (the bank in the above example) and ask to be sent some JSON, but browser cross-domain rules prevent scripts from the attackers domain from reading the reply, so the JSON is safe because it will never be eval()ed.
  • Script-Tags: The attacker can embed a script tag pointing at a remote server and the browser will effectively eval() the reply for you, however it throws away the response and since JSON is all response, you're safe.

It's the last of these arguments that is suspect. The dynamic nature of JavaScript will let you redefine how the browser evaluates the JSON.

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>

As a demo, I've redefined the Array constructor in this page, so if you're on Firefox, you should get an alert dialog from the following script: . Update: This was killing Google Analytics, which we added later, so I've removed the array hack script from this page.

(If you're reading this in a blog aggregator, then the script above should have been stripped out, so it won't work. You need to try it on this page. If the script has not been stripped, get a new aggregator - your current one has a horrible security flaw.)

The long and short is that JSON is not safe in any system that uses cookies for authentication.

With DWR we use full JavaScript which is as vulnerable as JSON, however DWR's CSRF protection automatically uses the doubly-submitted cookie pattern to provide extra safety.

I'm by no means the first person to think of this; Jeremiah Grossman used it to break GMail over a year ago.

Update: If you are doing JSON 100% properly, then you will only have objects at the top level. Arrays, Strings, Numbers, etc will all be wrapped. A JSON object will then fail to eval() because the JavaScript interpreter will think it's looking at a block rather than an object. This goes a long way to protecting against these attacks, however it's still best to protect your secure data with un-predictable URLs.



Re: JSON is not as safe as people think it is

Joe,
good points, I've been testing this and will certainly update my post but as far as I can make out this attack only applies to a subset of JSON. A JSON api returning this, for example
{
  "Image": {
    "Width":800,
    "Height":600,
    "Title":"View from 15th Floor",
    "Thumbnail":
    {
      "Url":"http://scd.mm-b1.yimg.com/image/481989943",
      "Height": 125,
      "Width": "100"
    },
  "IDs":[ 116, 943, 234, 38793 ]
  }
}
results in the following error when included via a script tag.
invalid label  "Image": {\n
I can get your attack to work if the json data looks like this (i.e. no labels),
[12,14, [45,56]]
but anything with a label errors out. It seems that this attack only applies to pure array structures. Is this in keeping with what you are seeing?
Rob

Re: JSON is not as safe as people think it is

I think you are getting invalid label because the Javascript interpreter can't distinguish between an object and a code block, so it thinks the foo: is a label.

In the script-tag based case the interpreter can tell the difference, (can't think why right now).

You can wrap the JSON in () to force the interpreter to think of it as JSON and not a code block.

e.g.

({ "F":[ 43 ] })

Re: JSON is not as safe as people think it is

Those planning on writing future-proof exploit code should note that 'setter' (and 'getter' for that matter) is deprecated. See this link for more info.

Using the new syntax; the sample code looks like this:

function Array() {
  var obj = this;
  var ind = 0;

  var getNext = function(x) {
    this.__defineSetter__(eval('ind++'),getNext);
    if (x) alert("Data stolen from array: " + x.toString());
   };
   this.__defineSetter__(eval(ind++),getNext);
}

Re: JSON is not as safe as people think it is

Hi Joe, I think it's great you are being wary of JSON for personalised data. However there are a couple of things you mentioned I don't totally agree with. You use the example of an array, however the syntax you used isn't actually JSON. http://json.org clearly defines JSON and stuff has to be wrapped in { } to be valid JSON. I discussed in a post last year (http://kid666.com/blog/2006/12/23/security-ajax-json-satisfaction/) about why arrays are vulnerable where objects are not. It comes down to the syntax used to allow Javascript to have multi-dimensional arrays. If you want to do secure JSON the best thing is to use a secure key which comes from the domain pages which consume the JSON and is validated on the server. This is usually implemented by using a server side language to print the 'key' to the page calling the JSON. A cookie is used to store a user unique value which is hashed with a secret. This value is passed as an HTTP argument with the JSON. Since each user has their own key a 3rd party can't call the JSON as that user successfully.

Re: JSON is not as safe as people think it is

I have updated the Safe JSON post to reflect these concerns. Many thanks for pointing them out and please let me know if you think that there are still vulnerabilities, Rob

Please delay redefinition of Array()!

By executing that Array() redefinition you wreaked havoc with my coComment extension, which relies on creating an Array at some point, resulting in a very long series of alert() boxes. Please consider delaying execution of that code!

Re: JSON is not as safe as people think it is

As far as I know, if you used the related JSON library* (not the formatting alone), you can prevent malicious Javascript from being parsed. (*): JSON.parse(...) More details on this post : http://blog.cotasson.info/?using-prototype-and-json

Re: JSON is not as safe as people think it is

That won't protect you from someone else embedding CSRF + the JSON/Array hack in their page and leaving out the JSON library.

Re: JSON is not as safe as people think it is

@Tom writes:
JSON and stuff has to be wrapped in { } to be valid JSON

Is that so? According to the RFC: "JSON-text = object / array"

Re: JSON is not as safe as people think it is

Either way, it doesn't matter whenever JSON contains an array an attacker can steal it.

And before someone suggests restricting JSON to objects, it's only a matter of time before someone works out how to break objects in the same way.

Re: JSON is not as safe as people think it is

This statement is strictly true: JSON is unsafe for anything but public data unless you are using unpredictable URLs. It is true for all data formats. There was never a good reason to believe that JSON reduced the need for vigilant design.

Re: JSON is not as safe as people think it is

I totally agree with you, about good application design.

My worry is that so few people understand the issues. Take a look at the comments, particularly on the Ajaxian thread, and you will see a lot of confusion, and the confused are creating insecure webapps.

Re: JSON is not as safe as people think it is

I wrote an article about how one can protect a webapp from this kind of hacks. It'slocated at solutoire.com

Re: JSON is not as safe as people think it is

Hi, I would like to introduce you to a new concept http://www.visualwebgui.com which eliminates most of AJAX soft spots by simply returning back to server based computing but still having a dynamic AJAX based UI. Guy

Re: JSON is not as safe as people think it is

Any other recommendations on how are we gonna post "array" from javascript to server?

Re: printing JSON file

Anyone know how to print JSON file in the servlet? {"markers":[ {"placemarkid" : "", "latitude" : "" , "longtitude": ""}, {"placemarkid" : "", "latitude" : "" , "longtitude": ""}, {"placemarkid" : "", "latitude" : "" , "longtitude" : ""}, {"placemarkid" : "", "latitude" : "" , "longtitude" : ""} ] } i know use out.println(), but how to print... How to print it in the servlet??

Re: JSON is not as safe as people think it is

OK I am a little confused. If your application is using personal data or some other information that is worth stealing, surely you must log on first? Right? In that case if somebody can spy on a session to get the session cookie, won't they be able to get the password by the same means? Wouldn't cookie authentication + SLL solve all these issues?

Articles

Nice informative article. thanks for sharing and keep sharing such kind of articles, as these articles really helpful for experienced and new comers.

Re: JSON is not as safe as people think it is

Many thanks for your interesting articles on javascript security. One small problem with this article - on visiting the page I get a tirade of alerts, effectively bringing down the browser. Perhaps you'd better redefine Array on demand, so that browsers augmented with extensions don't have problems.

Re: JSON is not as safe as people think it is

Agreed completely. I'd like to be able to cite this page as a reference for this attack, but at the moment I can't without warning people to disable Javascript first.

Re: JSON is not as safe as people think it is

I don't totally agree with. You use the example of an array, however the syntax you used isn't actually JSON.

Re: JSON is not as safe as people think it is

I think this article is pretty important stuff, and I want to link to it, but it's exceedingly yucky that it comes up with zillions of dialog boxes I have to click through when the page loads. Could you change it to only do that when you click a "test this" button?

Re: JSON is not as safe as people think it is

The later addition of Google Analytics caused the script to fight with Google's scripts. Oops. I've fixed it now.

Re: JSON is not as safe as people think it is

Using an object as a wrapper is not a solution on its own. Take this for example: {foo:['data','goes','in','here']} That will not be invalid. It will be treated as a label "foo" followed by an array containing the data.

Practical CSRF and JSON Security

What’s all this fuss about For the last couple of years JSON as a data transmission format has been gaining popularity. Fueled by a new generation of JS centric application developers, who didn’t grow up on XML files, but rather light weig...

Add a comment Send a TrackBack