Stateless CSRF Protection in an SPA

Coming from a traditional full stack background where both the frontend and backend were not separate apps, protecting against CSRF attacks when the two ends are separated, was a little confusing for me. How do I pass the CSRF token to my frontend app? Should I make an endpoint that generates a CSRF token? How is that even safe when anyone could access it and get one? It was a little difficult to find a straight forward answer but after a lot of reading I think I figured it out.

When should you use a CSRF token and when do you not have to?

If you’re building an app that saves something like a JWT in a cookie for authentication, you’ll need to protect against CSRF attacks. If you’re building something like a shopping cart, you’ll want CSRF protection, even in cases where the user isn’t logged in or authenticated yet. You don’t want an attacker to add something to someone’s cart without them knowing.

However, if your app doesn’t deal with any kind of sensitive information, you might be ok without it. For example, if your app doesn’t have an area where users are logging in and accessing protected information, you likely don’t need to worry about CSRF attacks. You might want to prevent abuse in other ways, like limiting certain actions or requests. But for the most part, if users of your site are making requests that add or modify data that isn’t behind a login, you don’t need to worry about CSRF protection. Why? Well, the main point of CSRF protection is to prevent a user from unknowingly making an action on a website while authenticated (according to OWASP). As an example, something that you likely don’t need a CSRF token for is a public contact form. It’s main purpose is to send an email to the site owner or maybe add an entry to a database. Though you will likely want to prevent abuse in other ways like I wrote above.

How to implement CSRF protection in an SPA

On the initial page load of your app, you’ll want to make an ajax call out to your API that generates the CSRF token for you and returns it in a json object. This request should also generate a secure http only cookie with the token stored in a JWT. The CSRF token should only be generated and returned if the user doesn’t already have one.

Read the token from the json object in javascript and store it in localStorage. Then on any future request that involves some kind of modifying action (POST, PUT, PATCH, DELETE, etc…), send the CSRF token along in the request.

Lastly, on the API server check that the CSRF token in the JWT cookie matches the CSRF token that was passed in the request. If it matches, allow the request to continue, otherwise prevent it.

Set up CORS

CORS (Cross-Origin Resource Sharing) allows you to control which domains can make certain requests to your site. By enabling this on your api server, you will prevent attackers from making ajax calls to your endpoint that initially generates and returns the CSRF token. You’ll want this to prevent attacks where the user hasn’t visited your site yet and doesn’t already have a CSRF token. If you want to know how to set up CORS take a look at this tutorial on setting up CORS.

And that’s it. From here on out you’ll be protected against CSRF attacks! If you have any feedback or questions, feel free to leave a comment below!