Preventing CSRF efficiently
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.

21 Comments:
great article!
In my opinion there's risk assessment vs. possible impact of a csrf attack. also i think that there's no definitive way to stop it. You can use a combination on referrer checks, double sessions and tokens - at least for a less skilled attacker there would be no way to circumvent this combination.
At least you would need that much effort as an attacker that you'd possibly chose another layer for you attacks.
The captcha solution is not optimal in my opinion - also captchas and pwntchas are having a little arms race these days. And captchas definitely bring severe accessibility issues on your site.
Greetings,
.mario
CAPTCHA is not useful as an authentication technique. It only protects against automation. It proves that a human is there, but not which human. An attacker using a cross domain leak like MHTML can still retrieve the image and answer the challenge.
If you aren't worried about the risk of a cross domain leak like MHTML, then you don't have to go to the trouble of putting a token in -every- URL either, you can just stick hidden fields in the critical forms.
rezn: I am worried about cross domain leakage, that's the reason I wrote this item. People who aren't could just read PDP's article and would then be happy.
I agree that it's possible to retrieve CAPTCHA images using MHTML bug however whether it is possible to answer them largely depends on their quality. A good CAPTCHA will be very hard to solve, or am I missing something?
Actually I'm not that into the dark matter of solving CAPTCHA's automatically ;)
No, what I mean is that an attacker who is exploiting CSRF together with an MHTML-like cross domain leak can retrieve the image, LOOK at it, and then answer the challenge.
The first XMLHTTPRequest can retrieve the image and then another XMLHTTPRequest can send that image out over the internet to the attacker, who can take a look and send another attack with the correct response to the challenge.
The one thing is that I haven't tried retrieving an image specifically with MHTML, but I don't see why it would fail. Even if it does, there will be other similar ways to bypass same-origin with XHR and some of them are sure to be able to retrieve images.
rezn
What is the difference between the solution you are proposing (which I like, BTW) and client-side certs where I (the web server) know I'm talking to a client I want to talk with (since I trust their certificate). Your is lighter, but isn't it the same principle at the end of the day? Plus the cert-based solution provides end-to-end integrity and confidentiality (SSL/TLS transport).
Client SSL certs win, definitely, as long as they are verified with every request. However, managing certificates for even a small userbase is quite difficult. Distribution and revocation are huge problems that are very difficult to solve securely. If such an infrastructure is already in place, you don't have much to worry about when it comes to XSS or CSRF, as you need to have the cert to even connect to the webserver.
Which brings you back to distribution. The best way to hack such a system is to hack the cert distribution process, which is undoubtedtly done via a webapp.
rezn
I typed too fast. I mean, you don't have to worry about someone stealing your cookies. You still have to worry a lot about CSRF. Cause CSRF is done by the browser that has the cert instaled in it. So ClientSSL certs don't actually help at all with whats being discussed here. Doh.
rezn
ChrisP: Sorry that has nothing to do with this. Look at what rezn said.
rezn: Wait a second, you and I made a huge mistake. I don't think it's possible for an attacker to retrieve the CAPTCHA image, except if the system is implemented badly.
I agree that via. MHTML bug you can retrieve the path of the image but not the actual source code. The path however is pretty useless for many reasons.
Firstly, it should never be allowed for arbitrary users to view those images.
Secondly, if an attacker could do the latter due to a flaw in the sites session management, he would just get a new generated image.
So yes, that will fail and actually I don't know why other approaches should work since grabbing a CAPTCHA has nothing to do with breaking the same-origin policy.
Again, let me know if I missed anything.
Btw: please stick to one nickname, otherwise it'll get to difficult to follow our discussions.
hmm, I still don't see something new what have not been suggested in previous papers (CSRF and session riding).
CAPTCHAs raise the bar, and ensure that the owner of the session keys it in, otherwise you need a XSS vulnerablity in the form containing the CAPTCHA itself. Hence CAPTCHAs serve the same way as an additional security token (hidden field), both already mentioned in the session riding paper.
Client certs are also no countermeasure 'cause you run into the same problem as with cookies, basic/digest authentication: they are send automatically which is exactly what CSRF/session riding drives.
There're various ways to raise the bar. But to make it secure and usable for the client, only additional tokens either form-based or as URL-rewriting do the job. It's up to the developers (servlets, frameworks, whatever) to provide ready-to-use libraries for secure session managment.
Still waiting for that, instead of re-phrased old suggestions ...
I apologize for the nickname mess. Blame google, as its their damn fault. I'll be more careful and always be anonymous from now on.
On the CAPTCHA. MHTML allows you to make an XHR for any resource that the target's browser has access to, which includes the image of the CAPTCHA.
So, attacker makes a webpage that contains a script which uses XHR/MHTML to request the URL of the CAPTCHA image, and then another XHR/MTHML req to send that data out over the internet to someplace they can decode/view it.
However, it turns out the XHR isn't great for retrieving images. As always, there may be some further hacks that you can use. This document has some ideas: http://www.cgisecurity.com/lib/XmlHTTPRequest.shtml
So, a CAPTCHA helps at preventing CSRF, but its not nearly as good as requiring a user to enter their credentials. It doesn't prove identity, just that a person is in the loop.
rezn
Hi! Just talked with christ1an about the captcha problematics.
Imagine the following: The captcha is not just one image but four/five/six separate images which are sorted via inline CSS or Javascript. Also you don't embed the images via src="/bla/image.php" but via temporarily saved images like src="/tmp/786868767/567578.gif" - what could an attacker possibly do to be able to circumvent this?
rezn: Why should XHR not be suitable for retrieving images? If you know the path of an image you can of course grab the source and rebuild it.
Therefore it is necessary to prevent attackers from being able to do so. Actually, this is pretty tricky but not impossible. I think what mario suggested should work.
@Anonymous: Obviously you didn't understand a single line of what I said. Thank you though...
And what kind of trick do you want to perform to read an image with XHR?
XHR can only receive text data
> @Anonymous: Obviously you didn't understand a single line of what I said.
Probably that's the reason, but then I guess that there're more people having this problem :-/
May you give some explanations of your text to enlighten me too? But see below first.
_
.. talk about solutions that do not work or only to a limited degree. ... suggest to use .. unique tokens, embedded somewhere in the HTML source or the URL ..
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, ..
_
We agree here.
_
.. such as cross domain Ajax due to the MHTML bug in Internet Explorer.
_
The MHTML bug is -- as its name implies-- a browser bug.
Should web applications take care of browser bugs? I'd argue no 'cause we're talking about web application security and not browser bugs.
We also agree that the refer(r)er and GET vs. POST solutions are useless or limited too.
_
.. there are in fact ways to prevent CSRF efficiently on condition that they are implemented correctly.
_
Yes I agree with that. The keyword here is correctly.
_
Since the attacker has full read access to that site, he is now able to grab the token and go on with his work.
_
hmm, that's probably the part were I don't understand.
If the attacker is able to grab the token (MHTML, XSS, whatever), then he's in the game. Doesn't matter then how secure the token is.
_
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.
_
That's what a security token is (and already explained in earlier papers). Got that?
_
The token would be generated in ..
_
If you "got that" (see above), then the gneration of such a token just needs to fit one requirement: unique (for each page at each time, to be more precise).
That's what needs to be done for a secure session management, and what most (I guess currently all) frameworks miss.
_
Unfortunately developers could fall into several traps ..
_
Yes, may be that's the reason why they don't implement secure session management, though leaving the risk/threat to the user ..
I don't blame developers (but script kiddies:), 'cause this is not a simple task. But if you look at state machines (mealy, moore) you see that the theory for that is ready to use since decades ;-)
So I finish this with your words:
"but it requires to work accurately."
(assuming that you meant that developers work accurately:).
I still don't see something new/better in your description than already sggested. Hence I follow your "you are more than welcome to leave a comment"
My comment is not meant as a personal offence.
@Anonymous
The MHTML bug is -- as its name implies-- a browser bug.
Should web applications take care of browser bugs? I'd argue no 'cause we're talking about web application security and not browser bugs.
I'm sorry but I don't agree, although I'm aware of the fact that a lot of people think so. We are talking about preventing CSRF and therefore we must consider existing browser bugs, since that's what some of those attacks we try to prevent are based on.
hmm, that's probably the part were I don't understand.
If the attacker is able to grab the token (MHTML, XSS, whatever), then he's in the game. Doesn't matter then how secure the token is.
Yes that's correct. Hence we need to stop him from doing so, which is what this posting is all about.
That's what a security token is (and already explained in earlier papers). Got that?
Sorry but thats simply wrong. I don't want to rule out that you may have read a paper that I didn't however I've read a lot and they all proposed to add a token on security relevant tasks only, which is the only reason anybody started arguing that these techniques can be defeated due to MHTML bug.
The whole MHTML thing relies on the condition that the attacker can send his victim on a site where a token is not needed yet but generated and embedded in the source. It doesn't matter whether its unique on each request or not.
Moreover I don't think it's that difficult and time consuming to implement this if you have a clue about application design.
By the way, who am I talking to?
we must consider existing browser bugs
Why should web applications fix browser bugs?
If that is the reason for your suggestion, then I'm out of the discussion. Sounds like I joined the wrong one, sorry.
.. they all proposed to add a token on security relevant tasks only ..
Might be true, another reason to teach people to separate security relevant tasks from gimmics ;-)
who am I talking to?
@Anonymous (google's most famous user;-)
cu elsewhere ..
"Should web applications take care of browser bugs?"
Should they? No! But do they have to? hell yes! I have been creating rich high level applications for over three years and man how many browser bugs did I have to workaround - IE CSS bugs, proprietary JavaScript stuff and even security stuff!
As said should? No! Must? Yes!
Thats was a very well-written article christ1an. Personally, from my point of view, i do agree that CRSF can be prevented and yes it definitely takes a lot of effort from the developer themselves too. The solution you gave was also good. As for the captcha part, i got to agree with you on the usability against security issue. A rolling captcha will be best for a fully secured system.
http://hackathology.blogspot.com
@Anonymous: Webapplications don't fix browser bugs, they should just keep them in mind.
I'd really like to know why you think to have joined the wrong one then.
Might be true [...]
Yeah it is in fact true. So whats the point of this discussion? If you had read attentively you'd perfectly know that you're blaming the wrong one.
Of course this is nothing against you personally, I'm really thankful that you state your opinion, however you've proven that you simply didn't understand the message of this item.
@hackathology:
Thank you very much! Actually I'm not fully sure whether we can really prevent CSRF (even with this approach) but we can at least get pretty close and thats exactly what serious developers are supposed to accomplish!
@mario
that's a nice idea with CSS capcha's, what I know is that bots don't fetch external CSS files.
-Jungsonn
@Jungsonn @mario
"that's a nice idea with CSS capcha's, what I know is that bots don't fetch external CSS files."
"Bots don't fetch external CSS files, Yet."
Post a Comment