back
16 comments
I just posted this on the issue:

----

I would be wary of solving the problem this way.

The problem is the object/array combo you're talking about is still perfectly valid JavaScript. However, due to the way browsers handle JavaScript, it's not currently possible to access the data from it. If you do implement it, you run the risk of a browser changing its behavior ever so slightly and destroying your security. ;-)

The way I've typically seen this done is to add a prefix of some kind to the data. The prefix can be valid JavaScript (eg: while(1); or throw 1;) or it can be text that JavaScript can't parse. Either way, the point is to ensure that a cross-domain request via the <script> tag will fail and not be able to get to the data. These solutions are slightly better because it's less likely that these constructs will become valid in the future (although still possible).

With that being said, here's the advice from the Browser Security Handbook, which advocates for a slightly different solution (http://code.google.com/p/browsersec/wiki/Part2):

Note: quite a few JSON interfaces not intended for cross-domain consumption rely on a somewhat fragile defense: the assumption that certain very specific object serializations ({ param: "value"}) or meaningless prefixes (&&&START&&&) will not parse via <SCRIPT SRC="..."> or that endless loop prefixes such as while (1) will prevent interception of the remainder of the data. In most cases, these assumptions are not likely to be future-safe; a better option is to require custom XMLHttpRequest headers, or employ a parser-breaking prefix that is unlikely to ever work as long as the basic structure of JavaScript is maintained. One such example is the string of )]}', followed by a newline.

Thanks for commenting ... When we discussed it in #documentcloud, that was the consensus we came to as well -- it's a bit silly to slightly tweak your JSON in this fashion, when browsers are still opening holes to make it executable in other ways:

http://directwebremoting.org/blog/joe/2007/03/06/json_is_not...

Doing a `while(1);` would be much more robust.

It seems to me the suggestion to require the 'X-Requested-With:XMLHttpRequest' header to be set is the best way to handle this, much better than some JS tweaking. It doesn't make any assumptions about javascript syntax or implementations. And the only way I can think to get around it is some http request splitting exploit via a proxy.

If you only return data to XHRs then you're protected by same origin policy and all <script> tags will get no data.

-EDIT-

When I first read "a better option is to require custom XMLHttpRequest headers" I thought this is what they were talking about. After a second look they probly mean setting a custom header yourself using the XHR object. This would work too but now I'm wondering if there's some way around my solution. Because why would they advocate a custom header if checking the X-Request-With header alone is enough?

- EDIT -

Was just in the shower and remembered why they probly don't advise to just check the X-Requested-With header. There are ways for an attacker to get around the XHR same origin policy with dns pinning/rebinding attacks. If you required a custom header with a session cookie which the attacker didn't have access to this would mitigate that kind of attack. EDIT: Nevermind, as long as you're checking the Host header a dns rebinding attack wouldn't matter.

You're absolutely correct. Origin or X-Requested-With (or any other custom header) are a good source of protection for cases like this.
This sounds like an even better fix -- nicely done.
You could just use the Origin header from CORS: http://www.w3.org/TR/cors/#origin-request-header
But how many browsers actually send the Origin header currently? I just checked Firefox and Chrome and neither sent it on a <script> or XHR request.
It definitely isn't set via <script>. I know it's sent when you make an XHR request via jQuery, so I assume you can set it as a custom header if you're rolling your own XHR.
good point, I was confused by the 'custom' recommendation too.
wouldn't it be possible for the attacker to alter the meaning of 'while' beforehand?
The assumption is that a browser won't let you re-define the meaning of low-level constructs like "while" and "1". The assumption is somewhat reasonable, but it could be violated in the future, which is why the Browser Security Handbook advocates against it.
+1 thank you for the clarification