32

I am in the process of writing a thick client web app using Angular.js (single page app) and was wondering what are the best practices for securing the app using a CSRF token.

Should I send a CSRF token when the app is first loaded then re use that token on every request? Should I have a mechanism to refresh the token? Are there other protections rather than a CSRF token that would make more sense for a single page app?

Adi
  • 43,808
  • 16
  • 135
  • 167
Olivier Lalonde
  • 5,039
  • 8
  • 31
  • 35

2 Answers2

18

Well here is how I ended up implementing CSRF:

On the first request, sets a CSRF token as a cookie. Every subsequent AJAX requests include the CSRF token as a X-CSRF-Token HTTP header.

Django has some nice documentation on how to do this cleanly with jQuery: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/

Edit: An alternative approach whitelists requests that contain the X-Requested-With header. It seems that's what Rails does. But as @damio pointed out below, the X-Requested-With is a security hazard, Django and Rails reverted back to not using it and forcing a token.

codefx
  • 103
  • 2
Olivier Lalonde
  • 5,039
  • 8
  • 31
  • 35
  • 1
    Maybe it's just me, but you _probably_ should have mentioned that you're using Django. Also, the `X-Requested-With` depends on your same-origin policy. If you've configured your server to allow any form of cross-origin requests, then it can be exploited. – Adi May 26 '13 at 05:46
  • I'm not using Django but I re-used the jQuery code in their documentation (I use Node.js). Is it safe to just use `X-Requested-With` if CORS is disabled? Also, is there a case where you would want to protect against cross origin requests if you explicitly allow them in your same-origin policy? – Olivier Lalonde May 26 '13 at 07:20
  • 1
    This is how the Angular.js team suggest you handle crsf tokens and they automatically extract the token from the cookie and add it to the request. – BenCr Sep 10 '13 at 18:04
  • 4
    The `X-Requested-With` is a security hazard, Django and Rails reverted back to not using it and forcing a token. https://www.djangoproject.com/weblog/2011/feb/08/security/ – damio Jun 09 '16 at 14:04
  • CORS is fine as long as `X-Requested-With` is not a header you put on the CORS whitelist. – Macil Apr 03 '17 at 23:09
  • The issue that made some web frameworks like Django stop allowing connections with `X-Requested-With` had to do with vulnerabilities in Flash which now only affect ancient versions, so unless ancient users are a priority, then using a header-check for CSRF protection seems more okay today. – Macil Apr 03 '17 at 23:11
16

You're in luck! Just about 2 weeks ago I was asked the same question, and after a bit of head-scratching, I came up with the following. Please keep in mind that this is not well peer-reviewed, so we'll see how the comments and voting goes. Personally, I think it's a good technique.

1. First request

Once you receive the first request to load your application, generate a secure random identifier and store it in a session variable on the server, then send it to the client. I'd embed it in the page just before </body>.

<script>setToken('<% print SESSION[TOKEN] %>');</script>
</body>

Your setToken() would assign the token value to the variable in which you'd keep the token.

2. Subsequent requests

With each request you'd add that token to the list of parameters, like token=TOKEN, and the serve would check it against the one stored in the session variable.

3. Refreshing the token

It's not really mandatory, but it's a good idea to refresh the token every once in a while, say 15 minutes. When you get a request after the token expires (you have the expiry time in the session variable), you respond to it normally (act optimistically) but in the response you tell the client that the old token has expired and it should start using the new one.

The client reacts to that by telling the server that it now knows the new token, once the server gets that request it starts using the new token and responds to the client with an OK message, once client receives 200 OK it means that they're both in sync and the client starts using the new token.

Note: A key element in the process is HTTPS usage. You don't want a Man in the Middle to sniff the token. But then again, an MitM would be able to sniff the cookies and hijack the session anyway.

Adi
  • 43,808
  • 16
  • 135
  • 167
  • 1
    Thanks for the answer. I am wondering though why it is recommended to have an expiry on tokens. I was under the impression that refreshing tokens was only a means to prevent accidental double form submits. – Olivier Lalonde May 26 '13 at 03:44
  • 3
    @OlivierLalonde Token expiry and refresh is part of a security-in-depth policy. You'd assume that _somehow_ somebody was able to predict the token, or _somehow_ the old token was compromised, so you'd want to limit the access window. – Adi May 26 '13 at 05:48