This spawned some great hallway conversations and got me really thinking about how we solve CSRF issues today. I kept coming back to an application I was working on recently that uses GWT and how I had to solve CSRF for that particular situation. See, normally the solution is to append a parameter on to the GET or POST request with a unique token that the server can verify against. Depending on who you talk to - sometimes this token has a lifespan of a session, and sometimes it is only a single request. Some would argue that for more sensitive applications you should use a per-request token and per-session tokens could be used in other places.
With that in mind, let's look at the GWT problem. You see in GWT, everything is handled by the Java -> Javascript compiler; which makes it very easy for Java Developers to create Rich UI Webapps. The flipside to this is that there is very little visibility (without a lot of research) into what is actually happening to your java code when you use GWT. Most developers just know that they put Java in and get a web app out. This presents an interesting dillema for security in general, but specifically for things like CSRF and DOM Based XSS.
Google has posted an article all about securing GWT Applications - while I normally commend google on their suggestions as they pertain to security in software, I could not believe what they were suggesting as a solution to CSRF. The full article can be read here - but here is an excerpt outlining the proposed solution.
"A common countermeasure for XSRF attacks involves duplicating a session cookie. Earlier, we discussed how the usual cookie-based session management model leaves your application open to XSRF attacks. An easy way to prevent this is to use JavaScript to copy the cookie value and submit it as form data along with your XMLHTTPRequest call. Since the browser's Same-Origin Policy will prevent a third-party site from accessing the cookies from your site, only your site can retrieve your cookie. By submitting the value of the cookie along with the request, your server can compare the actual cookie value with the copy you included; if they don't match, your server knows that the request is an XSRF attempt. Simply put, this technique is a way of requiring the code that made the request to prove that it has access to the session cookie."
Wait... what?
First thought that popped into my mind when I read that (and maybe you had the same thought) was that the proposed solution of duplicating a session cookie doesn't solve the problem at all. I suppose this could be the case in a Framejacking CSRF Exploit, that this would solve that issue - but if I send a crafted link to a handful of users to their bank to transfer 1 million dollars to my account like:
"Dear Sir or Madam, I am writing you from EvilHacker Bank and Trust to let you know about a new policy regarding our interest rates - please click the evil link below to get pwned.
http://evilbank.com/xfer?from=12345&to=12345&amt=1000000&forceApproval=1
Sincerely,
Doctor Evil
EvilHacker Bank and Trust"How does a cookie protect the user who is possibly already authenticated to the EvilHacker Bank application with a session cookie in their browser (and said duplicate session cookie). The browser is going to send both cookies because it does in fact, have access to them.
Well, that's completely unacceptable, I thought to myself. That doesn't protect the application or users of the application at all from CSRF. So I set out to come up with my own solution.
The answer, which turned out to be quite simple after a lot of research into how GWT builds in RemoteService interfaces and the javascript it uses to call services exposed to the client, was to make a slight modification to the ProxyGenerator (creates a Proxied implementation of the RemoteServiceAsync interface created by the developer). If you think of GWT Services in terms of RPC's this should make sense. I will likely post a follow-up detailing the solution and how to integrate it, but the meat of it is really this:
- App Server needs a filter or some means to setup the session on the first request made to the application. This is standard CSRF protection behavior.
- Caveat with GWT - GWT generally uses static HTML files and the Javascript generally doesn't have any reference to any existing interpreted code (ie: JSP, PHP, etc.) So the question became how do I tell the client application what the CSRF token should be. I chose to go with a cookie, but this could also be done as a separate RPC call to get the token (this presents it's own set of problems - they are not overcome-able)
- Created a filter (CSRFFilter) to generate a CSRF nonce when the session is created and set that as a cookie that gets sent back to the client.
- Client application reads the nonce from the cookie and alters the request to add a custom header containing the nonce.
- Server checks for either the presence of a request parameter or request header containing the CSRF nonce and verifies it against the value stored on the session (not in the cookie)
- A new nonce is generated every time a new session is created.
So what does this really mean?
It means that since SOP rules won't allow a Java Applet (post 1.6 update 8) or Flash App (unless it is setup completely incorrectly) to make a cross-domain request - you are safe from the attack embedded in a flash or java applet, and since an e-mail client can neither predict the nonce, nor can it send request headers - you are safe from CSRF.
It also means that you cannot have entry-point actions in your application. So if your model is to allow people to accomplish some action by clicking a link from their e-mail - you will want to exclude that service from this solution (or just about any other CSRF protection - here you have are probably going to have to resort to a hash that can be calculated as your CSRF nonce, which still provides ample protection against most threat agents and situations, but is slightly less evil-hacker-proof then using something completely random)
Now on to the argument about sensitive data and the per-request token model. I think this is severely overcomplicating an otherwise simple and elegant solution. A distributed phishing style CSRF attack would have to not only count on a pre-authenticated session but would also have to predict the nonce (which is tied to the authenticated session) which while not completely impossible is so improbable that it equalizes the threat completely. In other words, with this solution - you may have a single successful attack inside millions of years (depending on the width and PRNG used to create the nonce)
The complexity introduced in managing a per-request model in an Ajax application becomes a nightmare full of bugs.
So, in summary - I contest that the per-request model of CSRF nonces is overcomplicated and a complete overkill in 99.9% of cases (there may be the small edge-case where this makes sense).
Remember, the more complicated a Security Control is - the more likely there is a bug in the control that can be exploited to circumvent it.