Preventing Cross-Site Request Forgery (CSRF) in Node.js

Road to safety!

Mehran
4 min readMay 19, 2023
Generated using Lexica

Introduction

Cross-Site Request Forgery (CSRF) is a web application’s most dangerous security threat. This malicious exploit can trick users into executing unwanted actions on a web application where they’re authenticated. Understanding CSRF and knowing how to prevent it are vital for the security of your Node.js application. In this post, we’ll delve into the concept of CSRF, its impact, and how to prevent CSRF attacks in a Node.js application.

What is Cross-Site Request Forgery (CSRF)?

CSRF is an attack that tricks the victim into submitting a malicious request. It uses the identity and privileges of the victim to perform an undesired function on their behalf. A successful CSRF attack can lead to potential damage like data theft, change of user data, or even unauthorized actions.

For example, consider a banking application where a user can transfer money to another account. A CSRF attack on this application could cause the user to unintentionally transfer money to the attacker’s account.

How does CSRF work?

An attacker crafts a malicious web page or email, which contains a link or a form that performs a specific action on the target application when clicked or submitted, without the victim’s knowledge. As the user is already authenticated in the application, the browser sends the authentication cookies along with the request, leading the application to believe that the request is legitimate.

Preventing CSRF attacks in Node.js with TypeScript

There are several ways to protect a Node.js application from CSRF attacks. One common technique is using a CSRF token. This token is a random string that’s generated for every session or request. It’s embedded within the forms and links of the web page, and validated on every request.

We’ll illustrate how to implement this using csurf, a Node.js middleware for CSRF protection. We'll use express as the server framework, and TypeScript for writing the application code.

Step 1: Set up your project

First, create a new directory for your project, and initialize it with npm:

mkdir node-csrf-demo && cd node-csrf-demo
npm init -y

Next, install the necessary dependencies:

npm install express csurf cookie-parser typescript ts-node @types/node @types/express

Then, initialize TypeScript:

npx tsc --init

Step 2: Write your server code

Create a new file called server.ts. Here's the basic server code with CSRF protection

import * as express from 'express'
import * as cookieParser from 'cookie-parser'
import * as csrf from 'csurf'

// Initialize express app
const app = express()
// Middleware
app.use(cookieParser())
// CSRF protection middleware
const csrfProtection = csrf({ cookie: true })
app.use(csrfProtection)
// Routes
app.get('/form', (req, res) => {
// Validate the user before generting the new token.
// Pass the CSRF token to the view
res.send({ csrfToken: req.csrfToken() })
})
app.post('/process', (req, res) => {
// Processing the form data...
res.send('Form data processed successfully.')
})
// Start server
app.listen(3001, () => {
console.log('Server is running on http://localhost:3001')
})

How to get the CSRF?

The client must request to the /form endpoint to obtain a fresh CSRF token, which must be included in all subsequent calls. Please note, however, that this code is not production-ready. It is still necessary to include verification measures to secure a token acquisition from the server. These verification steps have not been omitted in this code to maintain simplicity.

Step 3: Validate the CSRF token

On the client side, when submitting a form, you need to include the CSRF token:

<form action="/process" method="POST">
<input type="hidden" name="_csrf" value="{{csrfToken}}">
<!-- rest of your form fields -->
</form>

Here {{csrfToken}} must be replaced with the actual CSRF token provided by the server.

When the form is submitted, the server checks if the _csrf field matches the CSRF token associated with the user's session. If it doesn't match, the server rejects the request.

Axios

If you use Axios to communicate with the server, you must include the CSRF token in the request's header.

// first, get the CSRF token
axios.get('/form')
.then(response => {
const csrfToken = response.data.csrfToken;

// then, send a POST request with the CSRF token in the headers
axios.post('/process', {
// your data here...
}, {
headers: {
'X-CSRF-TOKEN': csrfToken
}
})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
})
.catch(error => {
console.log(error);
});

Conclusion

Understanding and preventing CSRF attacks are fundamental aspects of web application security. By leveraging the power of the csurf middleware in your Node.js applications with TypeScript, you can implement robust CSRF protection. While this post focuses on one technique, it's important to remember that security is multifaceted, and CSRF protection should be part of a broader security strategy.

--

--

Mehran

Tech Team Lead | Cloud, Video & Microservices Expert | Insights on streaming innovations & programming. #ContinuousLearning