7 ms·
JSON is not as safe as people think it is
- PanMan 14y agoThis isn't about JSON at all, but about cross-site scripting.
- ipetepete 14y agoThe title threw me for a loop. I was like "OHNOES!" I must secure my JSONs probably with some XMLs. Then I was like oh...
- tlrobinson 14y agoNo, 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.
- MrUnderhill 14y agoThis 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/ http://ejohn.org/blog/re-securing-json/ (also from 2007).
- bmj 14y agoAFAIK, 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.
- jcampbell1 14y agoReally? You can make it so `var x = ['a']` does something other than create an array?
- bmj 14y agoNo, 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.
- MrUnderhill 14y agoThat'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 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 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 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).
- bmj 14y agoGotcha, thanks for the clarification.
- longlho 14y agoJSON is a standard to represent data in human-readable format, ofc there's no built-in security. Misleading title...
- duaneb 14y agoWell, 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.
- simonhamp 14y agoI 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.
- tzury 14y agoDouglas 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.
- fmavituna 14y agoThis 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-intensedebates-privacy-problems/ http://www.mavitunasecurity.com/blog/javascript-scope-and-in...
- jcampbell1 14y agoDouglas 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.
- Evbn 14y agoThe 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.
- 14y ago
- neya 14y agoI 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 http://en.wikipedia.org/wiki/JSONP
- fmavituna 14y agoYes it's indeed correct and document. CORS safely solves this problem. http://en.wikipedia.org/wiki/Cross-origin_resource_sharing http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
- neya 14y agoWow, thanks mate! I never knew of CORS before.. (I admit).
- jcampbell1 14y agoCORS 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.
- fmavituna 14y agoThe 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.
- zwily 14y agoThis 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.
- Zarel 14y agoI'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?
- zwily 14y agoI don't know. It looks like they're prepending some responses with )]}' now, which would cause a syntax error as well.
- swift 14y agoA 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 http://www.ietf.org/rfc/rfc4627.txt?number=4627) defines the root nonterminal in the JSON grammar as: JSON-text = object / array
- systematical 14y agoHow 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.
- jcampbell1 14y agoThis 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 https://yourserver.com/logout />
- tocomment 14y agoSo how do you prevent the logout thing?
- bsimpson 14y agoFlask 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.
- RyanZAG 14y agohowever 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.
- simonster 14y agoI 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.
- ricardobeat 14y agoMy 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.
- RyanZAG 14y agoWhile 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.
- r00fus 14y agoSo 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.
- robwgibbons 14y agoJSON is a container for data. It is no less safe than XML, HTML or plaintext. It's your application's architecture and basic security measures that define how "safe" your JSON is. CSRF tokens?
- ppierald 14y agoKnowing 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/3815c118a9e3e663215adba5e0ca626e8bdd5125 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.
- ppierald 14y agoKnowing 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' <-- whatever your auth cookies look like 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/3815c118a9e3e663215adba5e0ca626e8bdd5125 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.