Hacker News new | past | comments | ask | show | jobs | submit login
JSON is not as safe as people think it is (incompleteness.me)
41 points by morphics on March 27, 2013 | hide | past | favorite | 41 comments



This is a very old blog post, it's probably a good idea to add (2007) to the title to avoid confusion.

AFAIK, the ability to override the Array constructor has been fixed in all modern browsers.

Here's a blog post by John Resig about the same issue: http://ejohn.org/blog/re-securing-json/ (also from 2007).


AFAIK, the ability to override the Array constructor has been fixed in all modern browsers.

Is this true? I was able to do it in both Chrome and FF.


Really? You can make it so `var x = ['a']` does something other than create an array?


No, but if I do `var a = new Array();` I can have that put up an alert. The Resig post linked to above indicates that FF will complain if you try to redefine the Array function, which is not what I'm seeing FF 20 (beta channel) and Chrome 25.


That's not JSON though, it's executable Javascript. This whole issue is (was) about Arrays accidentally becoming executable Javascript. To elaborate:

Let's say I have a data API http://bank/sensitiveData which returns JSON:

  ["bmjs", "sensitive", "data"]
.. if your browser has a valid session. Now, if an attacker lures you onto his http://malicioussite, he may try to do:

  ajax.get("http://bank/sensitiveData).then(sendReceivedDataToMaliciousDatabase)
This will throw an exception, because the browser doesn't allow the cross domain requests to http://bank .

However, he _can_ insert a <script> tag that loads it (this is how JSONP works):

  <script src="http://bank/sensitiveData"></script>
This will load your sensitive data (assuming you have a session open). If, before this, the attacker has overridden the Array constructor in his site's Javascript, whatever code he has in that constructor will receive your sensitive data (and may send it to his malicious database).

Now, if my bank data API actually served your sensitive data as executable Javascript, that is:

  var bmjsSensitiveData = new Array("bmjs", "sensitive", "data");
Then you need to cancel your account and find a new bank immediately :) This would be loadable with the <script> tag on a malicious site as shown earlier, and subsequently make the variable bmjsSensitiveData available to the attacker's Javascript (overriding the Array constructor wouldn't even be necessary).


Gotcha, thanks for the clarification.


however it's still best to protect your secure data with un-predictable URLs.

Please, please, no. This is terrible advice - do not rely on security by obscurity! This is not even obscure - anybody can open the network monitor in Chrome and get the URL. You should be doing exactly the opposite - publish your URLs and get researchers to investigate them. Give a bounty if they break it and you'll have tons of researchers all happily fixing your security for you! Do not obscure this stuff.


I think the point is that each user's data should have its own URL, not that anyone should be able to get all the data from one URL. It doesn't matter if the user can get the URL; it points to their data, which they can get anyway. What matters is whether another website can get the URL and then pull the data out with a script tag, which is impossible if said website can't predict the URL.

You could also accomplish this by encapsulating things in an object literal (as suggested on the page); requiring POST or a header or returning a non-JS Content-Type; loading an HTML page on the site and using window.postMessage to send the data only to trusted sites; and maybe more ways as well.


My interpretation is that you should have URLs tied to data that is out of reach of the attacker. Instead of

    http://myapp.com/me/contacts.json
You'd have

    http://myapp.com/contacts/8df02390908dsf
Where '8df02390908dsf' could be a hash computed from the user's data, but a simple unique ID would suffice - as long as it's stored in document.cookie/localStorage where the third-party website can't access it.


While it's great to have that as an extra layer against attacks, as with all security by obscurity, the problem is when you rely on that detail to act as your security. If that hash is computed from the user's data, in 5 years time when they need to expose new data to some new platform, they may mistakenly choose to use that hash. Now every attacker has access to that hash - and your security that relied on keeping the hash secret is now wide open. There are many other possibilities of losing secrecy of a public url hash like this also.

The advice is still bad. Rather make your URL as open as possible and actually secure it for an attacker who happens to know it. If a user gets hacked and your response is 'well, nobody should have known the users ID!' then you're going to be looking pretty dumb.


This is why Google, Facebook, and others prepend JSON responses with while(1){}. Any browser trying to eval it will get stuck in an infinite loop, protecting the payload. That's what they did a couple years ago at least. It looks like gmail is now prepending with )]}' for some JSON calls instead.


I've been prepending with ], which as far as I know is enough to make any JSON data into a syntax error... is there a reason why these companies use longer things?


I don't know. It looks like they're prepending some responses with )]}' now, which would cause a syntax error as well.


JSON is a standard to represent data in human-readable format, ofc there's no built-in security. Misleading title...


Well, data has no inherent security or insecurity. But anyone expecting a format that revolves around being able to be evaluated by javascript to be MORE secure is living in a fantasy world.


Douglas Crockford's comment on that post:

    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.


This is wrong or awkward answer at best.

Your session based generated HTML (i.e. Your Credit Card usage history) is safe against these kind of attacks, also guess what your XML is safe as well and all other data formats that cannot be called and interpreted by JavaScript from another domain. Heck even session based generated images are safe and cannot be read from another domain, other than some rare bugs (they are security bugs and will be fixed, not design issues).

Another format I can think of that cannot keep session based information safe is JavaScript. I wrote about a real world example of this before (a vulnerability in IntenseDebate) : http://www.mavitunasecurity.com/blog/javascript-scope-and-in...


Douglas Crockford was wrong. This was a real issue back in 2007 that was specific to JSON. A proof of concept exposed gmail contact lists. It is not true for all data formats.

Douglas Crockford defends JSON to the point of making himself look foolish. He causally dismissed this real issue as a non issue, and has done the same with the U+2028/9 line ending issue.

Edit: It is worth pointing out that Google still prefixes their JSON with )]}' to protect users that are still using 2007 browers.


The technique of fetching remote content into JavaScript is dangerous. JSON is no more dangerous than any other JS.

Executing foreign code with eval is bad. Parsing foreign text is not. It was a problem when people misused browsers to conflate them.


This has nothing to do with executing foreign code with eval.

Here is how the the attack was performed. On my site, I rewrite the Array constructor. I then add

    <script src="http://gmail.com/mycontacts.json"></script>
Now you visit my site, and I now have stolen all your Google contacts, which are protected by your google password.

You see this has nothing to do with "eval".


That's why google starts their json with while(1); (or something like that) so any effort attempting to evaluate the json itself as javascript code will be stuck in an infinite loop.


I don't understand. Aren't there cross site scripting restrictions?


XSS restrictions generally apply to things such as making XmlHttpRequests from other sites. Loading scripts from other sites generally isn't restricted, since it's used for a variety of legitimate things such as loading jQuery from a CDN.


The problem here is still "the technique of fetching remote content into JavaScript", not json itself.


So how do you prevent this?


This isn't about JSON at all, but about cross-site scripting.


The title threw me for a loop. I was like "OHNOES!" I must secure my JSONs probably with some XMLs. Then I was like oh...


No, it's not. XSS is about injecting JavaScript in 3rd party sites to bypass the same origin policy.

This is about loading 3rd party JSON APIs on the attacker's site using other ways of bypassing SOP.

The first vulnerability (CSRF) isn't specific to JSON APIs, but the second one is.


Flask protects against this for you automatically if you use the built in JSON encoder.

This was probably more of a concern in 2007. I'm surprised that such an old (and largely irrelevant now) article made the front page.


Knowing that all user-data is required to have a user-credential, use this fact when issuing the urls to the browser that instantiate this request, make these urls specific to the user and cryptographically bound to their authenticated state.

session_id = '5f5513f8822fdbe5145af33b64d8d970dcf95c6e' ajax_endpoint_secret = 'this is a super secret!'

token = hmac.new( ajax_endpoint_secret, session_id, hashlib.sha1 ).hexdigest()

- or -

token = '3815c118a9e3e663215adba5e0ca626e8bdd5125'

then when your server emits the html page, your AJAX link looks like: https://example.com/ajax_api/3815c118a9e3e663215adba5e0ca626...

Here, we have cryptographically bound the AJAX url to the user's cookies, so the <script> trick won't work any longer as they don't have access to the token.

Second, you can have your server endpoint checking for the 'X-Requested-With: XmlHttpRequest' if you anticipate this to only be accessed via XHR. This is a pretty effective thwart of the <script> attack as well.

Lastly, ensure that your JSON response always is wrapped in curly braces {}. Never respond with [] From my experiments, all browsers interpret this as a code block and breaks processing when <script src=> runs.


How is JSON not safe for secure data as long as its going over SSL? And as others have stated this article is 6 years old.


This has zero to do with SSL. Let's say yourserver.com has a function like:

    if(user is logged in and SSL is on)
       emit user.credit_card_transactions as json
You may think everything is okay.

Now your user visits my site (With Firefox 2.0), and on my site I have:

    <script> // rewrite array contructor </script>
    <script src="https://yoursite.com/transactions.json">
Now I have effectively stolen your user's credit card transactions from your site. You see, from your server's point of view, the user is logged in and is connecting over SSL.

This is mitigated by modern browsers that don't allow rewriting the array constructor. If you care about users using FF 2.0, then you need XSRF protection. Many frameworks build this in by default.

XSRF can also be used for less nefarious things like: <img src="https://yourserver.com/logout />


So how do you prevent the logout thing?


I can't speak for JSON, but can someone try the same for JSONp[1], which many (legit) services use to bypass the same-origin policy?

For example,

    Your app - sends request -> Third party service
    Third party service - replies -> JSONp response
    Evil website's script on your app -> Hijack/modifies JSONp response -> steals credentials.
Is this possible? In all probability, it should be possible, right?

[1]It's a loophole (kind of) for executing Javascript across different domains. http://en.wikipedia.org/wiki/JSONP


Yes it's indeed correct and document.

CORS safely solves this problem. http://en.wikipedia.org/wiki/Cross-origin_resource_sharing


Wow, thanks mate! I never knew of CORS before.. (I admit).


CORS does nothing to address this problem. If you expose private data via CORS or JSONP you face the exact same problem.

CORS solves a different problem, in that you don't have to eval 3rd party code. If the remote service provider gets hacked, then your site may not get compromised.


The issue is an external file can be called from a 3rd party website via "<script src=" and if the format is something that JavaScript interpreter understands then it's possible to hijack the data (so called JavaScript/JSON Hijacking).

CORS solves it because you can WHITELIST the domains that can access the data, which is huge! Just like crossdomain.xml in Flash and ContenPolicy in Silverlight.

You are right though if the format is still JSON you are still vulnerable to the same attack, however CORS doesn't require you to use any format particularly, so it's possibly to use any other data format.


I don't think JSON is safe. In fact, as any good developer should with most technologies, I assume it's unsafe... that way I stand a better chance of securing my apps.


A commenter on that page claims that having an array at the top level is not valid JSON, but they're wrong. The RFC (found at http://www.ietf.org/rfc/rfc4627.txt?number=4627) defines the root nonterminal in the JSON grammar as:

  JSON-text = object / array


So is the solution to this to use the double-cookie send or nonces as the article states? I've been doing that in all my AJAX calls just to ensure no XSRF possibilities exist.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: