A page to show up #1 on Google when searching for "Jeremiah" (Currently #4). Only the prophet and TV show left! I have the edge, TV show is cancelled and the prophet isn't generating any new content.

The prophet, TV show, and that pesky Owyang guy going down!A page to show up #1 on Google when searching for "Jeremiah Grossman", and it FINALLY has!

Monday, January 22, 2007

Preventing CSRF when vulnerable to XSS

*The following code and concepts should be considered highly experimental and should be considered a work in progress. Not to be used for production websites.*

Web Worms (like Samy) targeting social networking websites (like MySpace) typically involve combining two attacks, Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF). An attacker posts JavaScript Malware (Web Worm) to their user profile web page because the website allows user-supplied HTML and the input filters didn’t catch the offending code. When a logged-in user visits the infected profile web page their browser is hi-jacked (user XSS’ed) to “friend the attacker” and post of copy the Web Worm code to their profile (user CSRF’ed) causing self-replication. There is no “Cross-Site” as part of the forged requests as CSRF implied, but that conversation is for another time.

As was case for MySpace and many other website, important features such posts to a user profile and friend’ing users, are protected from CSRF using session tokens embedded in URL’s or HTML Forms. Requests aren’t valid without a token. To defeat the CSRF solution, Web Worms first request a third-party page (on the same domain) to get a valid token and use it as part of a forged request. Since the attack is on the same domain, access to session tokens is can be easily achieved. This is why many people, including myself, have believe that CSRF solutions can be defeated when XSS vulnerability exist on that domain. However, there may be something we can do.

Without using browser exploits, JavaScript only has a few ways to access HTML data from another page on the same domain. Generally speaking, if we can prevent JavaScript on an XSS’ed web page from being able to read in session tokens from other pages, we might have something worth pursuing.

XMLHttpRequestwindow.openIFrame

If we can remove access these API’s, we may be able to prevent or make it harder for JavaScript Malware to bypas CSRF security. Enter prototype hijacking. The following proof-of-concept code effectively does this when called first at the top of the web page.

PoC Code is (Firefox ONLY!) Should this method be found workable, we can port examples to other browsers, namely Internet Explorer.

1) XMLHttpRequest

The Samy Worm used this method. This following function overwrites the XMLHttpRequest constructor so it has no functionality of any kind.

If I'm reading this correctly your saving off a reference of window.XMLHttpRequest for reuse later. Unless I'm missing something with your example you shouldn't be able to do this if I call this line before yours:

What I've wrote is not a hack (once overwritten reference can't be restored indeed), but improvement of the blocking method, that might retain XHR and make it available to your own code (with added verification against CSRF attack for example).

BTW: protection can be further improved by removing (hiding) token from page's DOM using JavaScript - this will work for pages loaded in popup/iframe.

Unfortunately it seems that overwritten prototypes are easily restored with "delete HTMLDocument.prototype.open". And a minor note: there is also createElementNS(), DOMParser and innerHTML that can be used as well to create elements.

To prevent tokens from being stolen (1) you can keep them away from pages which are vulnerable to XSS since these attacks are limited to the current domain. Embedding forms in iFrames is what Stefan Esser once discribed in his blog. http://blog.php-security.org/archives/48-CSRF-protections-are-not-doomed-by-XSS.html

I just wrote a post on another way to deal with stealing the token. Assuming the XSRF target and the XSS vulnerable page are NOT the same, you can use a key tied to the action to mess up the token. So you store a random number in the session, as usual, but give the user a hash of the random number concatenated with the key for the action you presume they're going to post to. To check the result, take the number from the session, the key for this action, hash, and compare to the hidden value dropped in.

Thank you. Trying to find SOMETHING a website can do. And it seems as though I have some more work to do on the JS side because people quickly found work arounds.

I believe Stefan Esser's concept was to host the forms on a separate domain as well. Using the same origin policy to help, which could work. The problem I saw was this would require rearchitecting websites and that might be an undue burden.

IFrames should just die. They were a bad idea when introduced in IE 3, and they are still a bad idea today. The only people who use them are the bad guys and adware vendors. Really, the best thing the W3C could do would be to deprecate them.