CSRF Tokens via AJAX

Why would you do something like this and is it a bad idea?

I get a lot of people questioning the wisdom of exposing CSRF tokens via AJAX, and asking if it’s a security hole.

// Add route to expose CSRF token via AJAX (Node.js / Express)
server.get('/csrf', (req, res) => {
return res.json({
csrfToken: res.locals._csrf
})
})

The tl;dir is no, it’s not a bad idea it’s a good idea and in fact you should probably be doing it if you are using CSRF tokens.

From a usability perspective, if you don’t provide a way to get CSRF tokens via AJAX, your website is probably going to be hellish to use because anyone who has multiple tabs open is going to run into errors about mismatching CSRF tokens.

On a website that uses CSRF tokens it can be much nicer experience for users if you have a onSubmit hook on forms to fetch the latest CSRF token before submitting a POST request.

A way to easily fetch CSRF tokens is also required for modern Single Page Application websites (e.g. universal sites built using React or Angular), which benefit from projection given by CSRF tokens just like traditional server-rendered only based sites.

In most implementations of CSRF tokens they are tied to sessions, even if the user is not explicitly logged in, and they can rotate over time.

This is why you might want to provide an endpoint to get them — because browsing in other tabs or other AJAX request for data can trigger token rotation, invalidating an old token automatically.

It’s important to note you should use HTTP Only cookies for your session tokens (which is best practice and provides protection against XSS attacks). However, CSRF tokens do not need to be stored in cookies — but they can be and unlike session tokens they do not need to be HTTP Only cookies.

Stateless way to handle CSRF tokens that don’t use sessions include the “Double Submit Cookie” method and the more complicated “Encrypted Token Pattern”.

The Double Submit Cookie method is much easier to implement than the Encrypted Token Pattern, and is a good approach if you are not implementing sessions. There are some caveats to them though, which are covered in the above links.

Whatever approach you use, where possible you should use a trusted library to handle this rather than rolling your own implementation.

CORS come ups a lot in discussions like this but often as a red herring.

The security really comes down to using an HTTP Only cookie for your session cookie and having a same domain policy for the cookie with your session token — which all browsers have supported since the dawn of time.

A caveat here is Internet Explorer exposing cookies to subdomains where other browsers don’t — which can cause an issue in some cases when using the Double Submit Cookie method to secure different sites on the same domain.

If some JavaScript on a remote site can’t read your session token — which it can’t if it’s in an HTTP Only cookie on your domain — then it can’t read your CSRF token either, even if you expose it via an AJAX endpoint!

You should be safe from CSRF attacks and you will have limited protection from some types of XSS attacks, but XSS is still a risk.

If someone finds a way to use XSS to execute arbitrary JavaScript on your website and create requests that come from your domain then neither CSRF, HTTP Only cookies nor session fingerprinting will protect you at that point — any action performed could be made to look just like it was triggered by a user.

A CAPTCHA on requests would provide some additional protection, although for irreversibly destructive or potentially expensive operations, external confirmation of an action (e.g. via email, SMS, etc) is a good idea.

If you are still worried an AJAX endpoint for CSRF might, somehow be a vector in a way you don’t understand, consider that a remote script that is able to execute a request from your domain and parse the response could equally just trigger a request to a page with a form on it and get the CSRF token from an <input> value on a <form>.

Equally, if someone is able to execute arbitrary JavaScript on your site and make and read requests back as the current user they are targeting, it would be a lot easier to just read the CSRF token from your DOM and extract it from a form already on the page than mess about with AJAX at all.

  • Use HTTP Only cookies and CSRF tokens to help protect against CSRF and XSS attacks.
  • When using HTTP Only cookies, providing a method to fetch a CSRF token via AJAX is not exploitable.
  • Providing a way to fetch your CSRF token via AJAX is a good thing, that leads to better user experiences without compromising security.
  • Consider adding a CAPTCHA and/or external validation to confirm actions as appropriate, as protection against XSS.

If you don’t use sessions on your site — or if you use them only for people who are signed in (i.e. you don’t have anonymous sessions), but still have forms accessible to people who are not signed in — you might want to consider the Double Submit Cookie method, which doesn’t use sessions or require you to store a value anywhere on the server.

The Double Submit Cookie method is an easy method to implement CSRF protection and works in most cases, however there is a caveat with it approach that mean it can’t be used in all scenarios, whether it’s suitable for your site or not not it depends on how your site works and how it is deployed.

See the OWASP Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet for more details and other ways to protect against CSRF.

Double Submit Cookie method (whether you are using sessions or not), but if you are allowing users to log in you should use still ideally use HTTP Only cookies for session tokens to protect against XSS attacks and session hijacking — as storing a session token in a JavaScript readable cookie or JSON Web Token is a vector for XSS attacks.

Written by

Software for news and media and civic tech. Cat herder. Director at Glitch Digital.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store