I imagine they simply remove the first 8 characters from the response string. eval("(for(;;);{})"); doesn't work, but eval("(" + "for(;;);{}".substring(8, 10) + ")"); works fine where 10 is the length of the response string.
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.
You should never be eval()ing json, thats the most dangerous thing you can do with it. All browsers have had native JSON parsers for years, which will choke on any code that's sent through them. These will be automatically employed by any js framework you're using, or I think its generally JSON.parse().
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.
I agree that eval() is evil and JSON.parse() is the way to go.
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.
This is good advice, but a non-sequitur in this case. This is not a code-injection attack, this is an information leak. It lets an attacker get around the normal constraints on fetching cross-site URLs. There is an EVAL happening, but it's being done by the attacker, not by you. And it's being done implicitly. The real problem here is a security hole in Javascript itself. It's too flexible for its own good.
"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.
Tangential: One of my favorite pieces about simplicity, laziness, dogmatism and getting things done is from Mark Jason Dominus[1]. His context isn't connected to this at all (it's about when and whether to use shell commands inside Perl scripts), but the larger point is very relevant: taking "Do the simplest thing that could possibly work" seriously can have surprising outcomes.
Interesting article, and I had a similar issue with #perl recently as well.
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.
They are adding executable code at the beginning of what is supposed to be a data structure. This solution is not elegant at all. It's an ugly hack that uses a side-effect of code to plug a hole in a rather convoluted security model.
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.