Yet another blog posting about cross site request forgeries and how to prevent them, however this time slightly different and not only dealing with approaches that have already been discussed numerous times and are likely to fail under certain well known circumstances. It's actually difficult to start on this but I'm prepared with a cup of coffee.
If you aren't familar with terms such as CSRF or session riding yet, please use Google or have a look on the CSRF FAQ, since I will skip that part.
During the last moths there has been a great deal of discussion on how to prevent cross site request forgeries and as far as I know, all of them ended confusingly and moreover imperfectly. Uninformed readers may get the impression that it's impossible to protect webapplications or - to phrase it more precisely - users, which however is not true at all. There are in fact various solutions which do work efficiently.
To provide you with a broader understanding on this matter, let's first of all talk about solutions that do not work or only to a limited degree. A lot of people suggest to use hard to guess unique tokens, embedded somewhere in the HTML source or the URL to make sure that the request is valid and intended by the user. I have to admit that this appears to be a working method but actually it isn't if you consider attack techniques that break the same-origin policy, such as cross domain Ajax due to the MHTML bug in Internet Explorer. Thus a versed attacker would gain the secret token and then trigger an authentic looking request to achieve his aim.
Next thing often recommended is checking the referer, which is insecure since referers can easily be spoofed in various ways (or simply not sent) but the reason I mention this is because you can implement this as an additional layer of security and refuse referers that are unexpected. So if implemented correctly it's a good idea to check referers.
For the sake of completeness I should probably add that using POST instead of GET is not a suitable solution at all but if you haven't realized that by yourself you should consider restarting at the very beginning.
Now as I said earlier, there are in fact ways to prevent CSRF efficiently on condition that they are implemented correctly. There is one technique that actually made me write this article and a few others which unfortunately are not liked by many people because they become lazy. One of the latter is CAPTCHA as a second factor credential, another similar approach is forcing the user to re-enter his password or thirdly sending a verification email / SMS.
Apparently those procedures are not liked by some people because they are inconveniently and decrease the usability. One the one hand that is right, at least up to a point, but on the other hand one can not equate usability with security. If you want a secure system you will sooner or later need to lower your sights somewhere else.
Of course everyone including me wants to hold those cut backs as low as possible and therefore I thought about one last approach, which can in my judgement considered to be secure. If anyone does not agree, you are more than welcome to leave a comment.
The idea is to use a session id based token which however can not be achieved neither by the MHTML bug nor any other kind of cross domain Ajax. To implement this correctly, lets just for a second reflect on the purpose of the MHTML story again. An attacker makes the victim send a request to the site in question where the victim will be authenticated due to a valid cookie. Since the attacker has full read access to that site, he is now able to grab the token and go on with his work.
The main weakness of all those former token based solutions is that they only protect security relevant tasks. If such a token was needed on any site and for any request inside the private area, there would be no way to grab a token without having it first. Got that?
The token would be generated in response to a successful authentication and then be added on each URL. I suggest to use the first five to six characters of the md5 encrypted session id in combination with a random keyword, which then will be cross checked on each request.
Unfortunately developers could fall into several traps while implementing this. Make sure that the token variable is added on every request, otherwise the system will be vulnerable. The same of course applies to XSS or redirection vulnerabilities, however these things remain in the hands of the developer and therefore are not calculable and can be under control.
So now it should be clear that it's not impossible to prevent CSRF but it requires to work accurately.