11.22.2010

CSRF - How much is enough?

One of the hot topics at AppSecDC this year seemed to be CSRF attacks and protecting against them. CSRF was brought up at least once in virtually every session that I attended at the conference (including several questions in my own ESAPI Presentation)

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:


  1. 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. 
    1. 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) 
      1. 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. 
  2. Client application reads the nonce from the cookie and alters the request to add a custom header containing the nonce. 
  3. 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)
  4. 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.

12 comments:

  1. I think you may have misinterpreted Google's suggestion for using the session cookie. What they are suggesting is copying the Session cookie value into a hidden field and before processing the request, verify that the value submitted via the hidden form field matches the value of the submitted session cookie (via the header).

    In theory, the reason that this works is that only your application should have access to the current session cookie value. As most modern session tokens are considered "sufficiently random and long" then why not use that as a token value. The benefit is that you do not have to worry about token generation, storage, should user's have multiple tokens, token expiration, etc.

    In actuality, it is a pretty simple and elegant solution.

    ReplyDelete
  2. I see your point - however, after some offline follow-up conversation, is it really necessary to have a parameter that is random at all with GWT? The fact that GWT Services (at least speaking of GWT-RPC) require the presence of custom headers set by the client (X-Requested-With, and custom Accept-Type) they should by definition be safe from CSRF.

    Since there is no way to force the user to forge a request that contains custom headers (post Java 6 Update 8) then you should be able to count on the assumption that GWT-RPC won't process the request (in fact it will generate an error) to ensure that your application is safe.

    The only circumvention that I can think of for this method (off the top of my head) is that A) Your page can be framed (clickjacking + csrf) or B) the page is vulnerable to DOM-Based XSS attacks; in which case the attacker could in theory generate his service call by invoking a custom evil function, which would allow him to craft the request with headers, would have access to all of the information in scope, and is basically undefendable in most browsers OOTB. Of course if that is the case, most traditional CSRF defenses will break down anyhow as the attacker could also extrapolate the valid token(s) for the next request and simply append it to his forged request using Javascript.

    ReplyDelete
  3. Java may be the main choice for enterprise development now, but it’s days are numbered as the only stalwart option to go with.

    Let’s face it, many of these so called “enterprise applications” could easily have been written much faster and with less overhead using technologies like Python, PHP, et al.






    open source training

    ReplyDelete
  4. Many "Enterprise Applications" have already been written in those languages - this article used my experience with GWT as an example - however, the same rules could be applied to *any* language.

    ReplyDelete
  5. Hi Chris,

    You talk about a follow-up detailing the solution and how to integrate it.
    Have you any code sample to post?

    thanks in adance

    ReplyDelete
  6. I am doing some additional research in the area of CSRF right now with a colleague of mine, so very soon there will be a ton of new info up about this subject. If you have a particular example you are looking for though, let me know and I will see what I can provide for you.

    ReplyDelete
  7. Well, I think you probably will not agree with the following post (http://groups.google.com/group/google-web-toolkit/browse_thread/thread/6b1c412fd053a096/f315f85b4d25c78f?lnk=gst&q=csrf#f315f85b4d25c78f) where Sripathi explains how to implement the solution posted by google.
    I'm working on a project building a sensible transactional Web application base on GWT : I'm studying the differents problematic such as security and others (performance historic, good architecture for future industrial developpments, ...). I use RPC mechanism and I'm very interested in build a secure application (prevention agqinst CSRF ans XSS attacks). I've read differents articles about it(random csrf token generation) and I would like to be sure of what I'm doing : use cookie to get the token from client and resend it in the request? ...

    ReplyDelete
  8. I think you probably won't agree with this solution posted in gwt discussion list (http://www.mail-archive.com/google-web-toolkit@googlegroups.com/msg55873.html) where Sripathi explains how to implements the "double-submit the session id" recommandation. I'm working on building a large scale web site (sensible transactionnal application) based on GWT framework. So I'm very interested in different problematics such as security and others (performance, history management, maintainable, ...). I've read a lot about CSRF and XSS attacks prevention and I search the best solution to integrate into my architecture, especially some code examples about it : server side, generation of random csrf token, add it to a cookie (after identification session ok), then, client side, get the token from cookie and set to the rpc service. If I use cookie for session id (encrypt cookie), do I have 2 cookies to create (one for identification, the other for csrf prevention)?

    ReplyDelete
  9. Your blog has given me that thing which I never expect to get from all over the websites. Nice post guys!

    ReplyDelete
  10. Super option, I think one of the best thing is

    ReplyDelete