Implementing CSRF Protection in Node.js from Scratch

Cross-Site Request Forgery (CSRF) is an attack that tricks an authenticated user into submitting an unwanted request to a web application. To protect a Node.js application from this type of attack, we can implement a CSRF token system from scratch. This article shows step by step how to do it.

1. Generate a CSRF Token

A CSRF token must be:

  • Unique for each session
  • Hard to guess

We use Node.js's crypto module:

const crypto = require('crypto');

function generateCSRFToken() {
  return crypto.randomBytes(32).toString('hex');
}

2. Store the Token in the Session

We will use express-session to manage the user's session:

const session = require('express-session');
const express = require('express');
const app = express();

app.use(session({
  secret: 'a_super_secret_string',
  resave: false,
  saveUninitialized: true
}));

Every time a user loads a form, we assign a new token if it doesn’t already exist:

app.use((req, res, next) => {
  if (!req.session.csrfToken) {
    req.session.csrfToken = generateCSRFToken();
  }
  next();
});

3. Include the Token in the HTML Form

The token is inserted in a hidden field:

<form method="POST" action="/submit">
  <input type="hidden" name="csrfToken" value="<%= csrfToken %>">
  <input type="text" name="message">
  <button type="submit">Send</button>
</form>

In the controller of the route that returns the form, we pass the token:

app.get('/form', (req, res) => {
  res.render('form', { csrfToken: req.session.csrfToken });
});

4. Validate the Token on the Server

When the form is submitted, the server must compare the received token with the one stored in the session:

app.post('/submit', express.urlencoded({ extended: false }), (req, res) => {
  const tokenFromForm = req.body.csrfToken;
  if (tokenFromForm !== req.session.csrfToken) {
    return res.status(403).send('Invalid CSRF token');
  }
  // Proceed with the request logic
  res.send('Valid request');
});

5. Regenerate the Token After Each Request

For increased security, we can regenerate the CSRF token after each valid request:

req.session.csrfToken = generateCSRFToken();

Conclusion

We have implemented a simple and working CSRF system from scratch in Node.js, without relying on dedicated external libraries. This approach ensures a good level of protection for authenticated forms in environments where full control over the implementation is desired.

Back to top