CSRF (Cross Site Request Forgery) tutorial
CSRF stands for Cross Site Request Forgery.
CSRF is along with XSS and SQL Injection one of the most common web attacks.
In short, a CSRF attack drive people visiting our site to perform actions they didn’t intend to.
A successful CSRF attack can lead to data loss, unauthorized activity, and data loss, so it’s definitely one topic to study carefully.
CSRF using HTTP GET requests
Let’s start with the simplest case of CSRF.
You have a database of cars.
Doing a GET request to /api/delete?id=1
will delete the car with id
1.
I visit another website that has an image with this source URL: https://yoursite.com/api/delete?id=1
Like this:
<img src="<https://yoursite.com/api/delete?id=1>" />
The browser connects to the URL to try fetch the image. The request is executed, the request is submitted and the car is deleted.
If the app requires authentication, if you didn’t perform logout from the website, then the request will be performed successfully, because the session is still active.
Due to how the Web works, any request to a domain automatically includes session cookies (unless using strict SameSite cookies), so the server thinks the request is legit.
You can easily try to replicate this with a Node.js app.
The simple solution to this is to always avoid GET
requests to perform any data manipulation, as requests like this just work using the GET
method. Use POST
.
This is not limited to images. iframes
for example lead to the same problem.
CSRF with forms
Any form in your app should be protected with a CSRF protection token.
https://github.com/expressjs/csurf is a CSRF protection middleware for Express.
Every time you render a form, you embed a CSRF token in the form, and you verify the token when you receive the form data.
For single-page apps (SPA), you can use an endpoint to retrieve this token, and store it into a cookie.
Solving CSRF using SameSite Cookies
The SameSite
attribute set on cookies, unfortunately still not supported by all browsers, lets servers require that a cookie is not sent on cross-site requests (like image source links or iframe requests), but only on resources that have the cookie domain as the origin, which should be a great help towards reducing the risk of CSRF (Cross Site Request Forgery) attacks.
This solves most of the common CSRF problems.
Modern browsers set cookies automatically to SameSite=Lax
, which is a balance between complete security but blocks requests like image source URLs.
It allows users to use a link and come to the website, and still have their session in place.
The most strict effect happens with SameSite= Strict
, where the session cookie is not recognized when coming to the website from a link.
I imagine this is optimal for banks, for example.
Solving CSRF using one-time tokens or password
Speaking of banks, many implement a one-time token to do even the simplest of the operations, and I’m happy about that.
Every time I want to send money I have to look at my phone their one-time token app, identify using Face ID, then copy the number I see to the computer.
There is just no way a late-Friday deploy can trigger a CSRF problem on a similar site.
I noticed GitHub requires my password to change the settings, for example.
Also Fastmail, a service I use for emails.
They are all crucial in their operations and we want them to implement all the right countermeasures. We should take inspiration from that.
→ I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter
→ JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025