

JSON is not as safe as people think it is - morphics
http://incompleteness.me/blog/2007/03/05/json-is-not-as-safe-as-people-think-it-is/

======
MrUnderhill
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).

~~~
bmj
_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.

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

~~~
bmj
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.

~~~
MrUnderhill
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).

~~~
bmj
Gotcha, thanks for the clarification.

------
RyanZAG
_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.

~~~
ricardobeat
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.

~~~
RyanZAG
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.

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

~~~
Zarel
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?

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

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

~~~
duaneb
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.

------
tzury
_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.

~~~
jcampbell1
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.

~~~
Evbn
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.

~~~
jcampbell1
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".

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

~~~
Zarel
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.

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

~~~
ipetepete
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...

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

------
ppierald
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...](https://example.com/ajax_api/3815c118a9e3e663215adba5e0ca626e8bdd5125)

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.

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

~~~
jcampbell1
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> />

~~~
tocomment
So how do you prevent the logout thing?

------
neya
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>

~~~
fmavituna
Yes it's indeed correct and document.

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

~~~
jcampbell1
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.

~~~
fmavituna
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.

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

------
swift
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

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

