CSRF Protection in NestJS: How to Implement It

Updated: December 31, 2023 By: Guest Contributor Post a comment

Securing web applications is crucial, and CSRF (Cross-Site Request Forgery) protection is fundamental in a NestJS application. This tutorial will guide you through implementing robust CSRF protection leveraging NestJS’s latest features and TypeScript.

Introduction

CSRF attacks involve tricking a user into sending unauthorized commands to a web application where they’re authenticated. In a NestJS application, CSRF protection can be effectively implemented using CSRF tokens that validate user requests. NestJS offers built-in CSRF protection that integrates seamlessly with Express or Fastify. In this tutorial, we’ll be working with the Express platform.

Setting Up the NestJS Project

npm i -g @nestjs/cli
nestjs new my-csrf-secure-app

After setting up the project, navigate to it:

cd my-csrf-secure-app

Integrating CSRF Middleware

First, let’s integrate the CSRF middleware by installing the required package:

npm i csurf

Within your application, set up CSRF protection by applying it as global middleware.

import * as csurf from 'csurf';

// main.ts
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(csurf());
  await app.listen(3000);
}
bootstrap();

Note that CSURF middleware requires either a session middleware or cookie-parser to read cookies, which you’ll need to install and set up accordingly.

Handling CSRF Tokens

The next step is to generate a CSRF token and provide it to the client. This is usually done by including the token in the rendered views.

import { Controller, Get, Req, Res } from '@nestjs/common';
import { Request, Response } from 'express';

@Controller()
export class AppController {
  @Get()
  provideCsrfToken(@Req() request: Request, @Res() response: Response) {
    response.render('form', { csrfToken: request.csrfToken() });
  }
}

Including CSRF Tokens in Client Requests

When submitting forms or sending AJAX requests, ensure the CSRF token is included. An example using a template engine form:

<form action="/submit" method="post">
    <input type="hidden" name="_csrf" value="{{csrfToken}}">
    <!-- form fields -->
    <button type="submit">Submit</button>
</form>

In AJAX requests:

fetch('/submit', {
  method: 'POST',
  headers: {'Content-Type': 'application/json', 'CSRF-Token': 'your-csrf-token'},
  body: JSON.stringify({data: 'your-data'})
});

Advanced Configuration and Strategies

In more advanced scenarios, you might want to customize the CSRF implementation. You can configure the CSURF options directly to control aspects like the cookie settings or the response on token validation failure.

app.use(csurf({cookie: {httpOnly: true, secure: process.env.NODE_ENV === 'production'}}));

For RESTful APIs that serve client applications separately (e.g., Single Page Applications), you’ll need to devise CORS and CSRF protection strategies that ensure both are correctly handled.

Testing CSRF Protection

Ensure your CSRF protection is effective by writing tests that aim to simulate CSRF attacks, either manually or using testing libraries.

import { Test } from '@nestjs/testing';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('CSRF Protection', () => {
  it('should prevent unprotected POST request', () => {
    const moduleFixture = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    const app = moduleFixture.createNestApplication();
    await app.init();

    return request(app.getHttpServer())
      .post('/submit')
      .expect(403); // Expects a Forbidden error
  });
});

Conclusion

Incorporating CSRF protection into your NestJS applications is straightforward with the built-in middleware and strategies provided by the framework. By following the steps in this tutorial, you can ensure that your application validates requests properly, keeping user data secure from CSRF vulnerabilities.