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.