Skip to content
NestJS ns security 4 min read

Security Overview

Security in a NestJS application is not a single feature you switch on — it is a set of overlapping defenses applied at the transport, HTTP, application, and data layers. Because Nest sits on top of Express or Fastify, you inherit the wider Node.js ecosystem of middleware and guards, and Nest gives you first-class building blocks (guards, interceptors, pipes, middleware) to wire them in cleanly. This page surveys the OWASP-aligned threats every API faces and maps each one to the Nest mechanism that defends against it.

The layers Nest gives you

Nest’s request lifecycle is the backbone of its security model. Each incoming request flows through middleware, guards, interceptors, and pipes before it ever reaches your handler — and each stage is a natural place to enforce a control.

LayerNest primitiveTypical security use
Transportreverse proxy / TLSForce HTTPS, terminate TLS
HTTP headersMiddleware (Helmet)CSP, HSTS, frame options
Cross-originapp.enableCors()Restrict allowed origins
AuthorizationGuards (CanActivate)AuthN/AuthZ, RBAC
ThrottlingThrottlerGuardBrute-force / DoS mitigation
InputPipes (ValidationPipe)Schema validation, sanitization

Wiring these globally in main.ts gives you a consistent baseline:

import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import helmet from 'helmet';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // 1. Secure HTTP headers
  app.use(helmet());

  // 2. Lock CORS to known front-ends
  app.enableCors({
    origin: ['https://app.devcraftly.com'],
    methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
    credentials: true,
  });

  // 3. Validate and strip every payload
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
    }),
  );

  await app.listen(3000);
}
bootstrap();

Mapping OWASP risks to Nest defenses

The OWASP API Security Top 10 names the failures attackers exploit most. Nest does not magically solve them, but it gives you the right place to put each control.

OWASP riskWhere it bitesNest defense
Broken authenticationStolen/forged tokensPassport guards, JWT strategy
Broken object-level authorizationAccessing others’ recordsCustom guards checking ownership
Excessive data exposureReturning whole entitiesSerialization interceptor + @Exclude()
Lack of rate limitingBrute force, credential stuffing@nestjs/throttler
InjectionUnsanitized inputValidationPipe, parameterized queries (TypeORM/Prisma)
Security misconfigurationMissing headersHelmet, disabled x-powered-by

Authorization with guards

Guards are the idiomatic place for authentication and authorization. They run before the handler and short-circuit with a 403/401 when access is denied.

import {
  CanActivate,
  ExecutionContext,
  Injectable,
  ForbiddenException,
} from '@nestjs/common';

@Injectable()
export class OwnerGuard implements CanActivate {
  canActivate(ctx: ExecutionContext): boolean {
    const req = ctx.switchToHttp().getRequest();
    if (req.user?.id !== req.params.userId) {
      throw new ForbiddenException('You do not own this resource');
    }
    return true;
  }
}

Hiding sensitive fields

A ClassSerializerInterceptor paired with class-transformer decorators prevents over-exposure of entity data such as password hashes.

import { Exclude } from 'class-transformer';

export class UserEntity {
  id: string;
  email: string;

  @Exclude()
  passwordHash: string;
}

Output:

GET /users/42
{
  "id": "42",
  "email": "[email protected]"
}

The passwordHash field never leaves the server.

CSRF and state-changing requests

CSRF only matters when the browser auto-attaches credentials — i.e. cookie-based sessions. Pure bearer-token APIs (the Authorization header is not sent automatically by browsers) are largely immune. If you use cookies, add the csrf-csrf (or csurf legacy) middleware and pair it with SameSite=Lax cookies.

import { doubleCsrf } from 'csrf-csrf';

const { doubleCsrfProtection } = doubleCsrf({
  getSecret: () => process.env.CSRF_SECRET!,
  cookieName: '__Host-csrf',
});

app.use(doubleCsrfProtection);

Tip: Choose ONE auth transport per surface. Mixing cookies and bearer tokens on the same endpoint multiplies your attack surface and makes CSRF reasoning much harder.

A practical security checklist

Treat this as a pre-deploy gate for every Nest service:

  • Transport — Terminate TLS, redirect HTTP to HTTPS, and emit HSTS via Helmet.
  • Headers — Enable Helmet with a deliberate Content-Security-Policy; remove x-powered-by.
  • CORS — Allowlist exact origins; never reflect arbitrary Origin with credentials enabled.
  • Input — Apply a global ValidationPipe with whitelist and forbidNonWhitelisted; use DTOs everywhere.
  • Rate limiting — Throttle auth and write endpoints with @nestjs/throttler.
  • AuthZ — Enforce both authentication and per-object authorization in guards.
  • Secrets — Load from environment/secret manager via @nestjs/config; never commit them.

Warning: A global ValidationPipe only validates inputs decorated on DTO classes. Endpoints that accept raw req.body bypass it entirely — always bind a typed DTO.

Best Practices

  • Apply security globally in main.ts so new modules inherit the baseline automatically rather than opting in.
  • Prefer Nest primitives (guards, pipes, interceptors) over scattered inline checks — they are testable and reusable.
  • Validate with whitelist: true to silently strip unknown properties and block mass-assignment attacks.
  • Use a parameterized ORM (Prisma, TypeORM) and never concatenate user input into queries.
  • Set restrictive CORS and CSP policies, then loosen deliberately — default-deny beats default-allow.
  • Keep dependencies patched and run npm audit in CI; most real-world breaches exploit known CVEs.
  • Log security-relevant events (failed logins, throttle hits) without logging secrets or full tokens.
Last updated June 14, 2026
Was this helpful?