EDIT: This is the correct idea. Search for shieldlen or safeResponse in the JS to see how it is implemented. There is always something interesting to be learned by digging through FB's client side code.
By eval()ing your json you are doing most of the attackers work for them. All that stuff in the article about mime types is redundant if you're eval()ing your json.
Basically, don't ever eval() anything, in any language.
From: http://www.json.org/js.html
"The use of eval is indicated when the source is trusted and competent. It is much safer to use a JSON parser."
FB is still using eval() if you look at their code. As the source of the JSON is their own service, and they can, therefore, trust it assuming proper sanitization; the same applies for my test case.
However, in this particular case this doesn't matter much, because the site itself controls where to load the JSON from. Manipulating the JSON response requires access to the webserver or the DNS, and in those cases the attacker could have manipulated the initial HTML response as well.
> By eval()ing your json you are doing most of the attackers work for them. All that stuff in the article about mime types is redundant if you're eval()ing your json.
This totally misses the context of the article. The article is about CSRF. That is, the _attacker_ downloads and executes the JSON.
This is _not_ about downloading the attacker's JSON! It is about how to construct the JSON in a way that it is unaccessible through a <script> tag from the attacker's site.
https://secure.wikimedia.org/wikipedia/en/wiki/Same_origin_p...
tl;dr Sometimes ugly is elegant, too.
[1] http://perl.plover.com/yak/12views/samples/notes.html#sl-3
They started with trying to fix a performance problem I didn't have, and then after my refusal to give them more information to fix a problem I didn't have, started insulting me.
Edit:
Come to think of it, this seems to be solvable by a much better method - the same one that is used to prevent standard CSRF. The problem is effectively the same.
Server A is a valid server. User logs into it and gains privileges. Then he visits server B, which is a bad server. B tricks the browser into sending a request to server A to do something (abusing the elevated privileges). The only addition with AJAX is that server B also manages to read the result of its attack. That wouldn't matter if it couldn't trick the server A honoring that page request in the first place.
You can easily solve this by signing your requests, effectively binding two pages on your server together. Normally, PAGE1 has a form (or JS code) that requests PAGE2. You simply need to enforce that only legal (yours) pages can do that. This is achieved by adding a token to PAGE1 that must be sent to PAGE2.
For example, token = hash(server_secret + PAGE2_identifier + user_id).
Upon receiving a request for PAGE2, the server will know all the arguments that went into creating that token, and re-generated. If the user-supplied token and server-generated tokens don't match, the request is denied.
As long as client side scripts from server B cannot read the token from server A, this should work even with AJAX requests. Since attacker (server B) cannot know server_secret, they will not be able to guess your generated token, and their requests to PAGE2 on server A will fail.