We use cookies to enhance your experience on the site
CodeWorlds

SSL/TLS and HTTPS Configuration - the Empire's defensive shield

Cybersecurity legionary! Architect Vitruvius knows that every province of the Empire needs solid fortifications. Just as defensive walls protected Rome from invaders, SSL/TLS and HTTP security protect our NestJS applications from network attacks. It's time to learn the art of building digital defensive walls!

SSL (Secure Sockets Layer) and its successor TLS (Transport Layer Security) are cryptographic protocols ensuring secure communication over the network. HTTPS is HTTP running over the SSL/TLS layer - it encrypts data transmitted between the client and server.

Generating SSL certificates

The first step in building defensive walls is obtaining certificates. In a development environment, we can generate self-signed certificates, and in production, we use Let's Encrypt or commercial providers.

Development certificates (self-signed)

1// scripts/generate-dev-cert.ts
2import { execSync } from 'child_process';
3import * as fs from 'fs';
4import * as path from 'path';
5
6const certsDir = path.join(__dirname, '..', 'certs');
7
8// Create directory for certificates
9if (!fs.existsSync(certsDir)) {
10  fs.mkdirSync(certsDir, { recursive: true });
11}
12
13// Generate private key and certificate
14execSync(\`openssl req -x509 -newkey rsa:4096 \\
15  -keyout \${certsDir}/key.pem \\
16  -out \${certsDir}/cert.pem \\
17  -days 365 -nodes \\
18  -subj "/C=PL/ST=Roma/L=Roma/O=Imperium/CN=localhost"\`);
19
20console.log('Development certificates generated in the certs/ directory');

HTTPS configuration in NestJS

1// src/main.ts - starting the server with HTTPS
2import { NestFactory } from '@nestjs/core';
3import { AppModule } from './app.module';
4import * as fs from 'fs';
5import * as path from 'path';
6
7async function bootstrap() {
8  const isProduction = process.env.NODE_ENV === 'production';
9
10  // HTTPS options - SSL certificates
11  const httpsOptions = isProduction
12    ? {
13        key: fs.readFileSync(process.env.SSL_KEY_PATH),
14        cert: fs.readFileSync(process.env.SSL_CERT_PATH),
15        ca: fs.readFileSync(process.env.SSL_CA_PATH), // Certificate chain
16      }
17    : {
18        key: fs.readFileSync(path.join(__dirname, '..', 'certs', 'key.pem')),
19        cert: fs.readFileSync(path.join(__dirname, '..', 'certs', 'cert.pem')),
20      };
21
22  const app = await NestFactory.create(AppModule, { httpsOptions });
23
24  // Force HTTP -> HTTPS redirect
25  if (isProduction) {
26    app.use((req, res, next) => {
27      if (req.headers['x-forwarded-proto'] !== 'https') {
28        return res.redirect(301, \`https://\${req.headers.host}\${req.url}\`);
29      }
30      next();
31    });
32  }
33
34  await app.listen(process.env.PORT || 3000);
35}
36bootstrap();

Helmet.js - HTTP security headers

Helmet.js is like a centurion's helmet - it protects the head (HTTP headers) against the most common attacks. It sets appropriate security headers automatically.

1// src/main.ts - Helmet configuration
2import helmet from 'helmet';
3
4async function bootstrap() {
5  const app = await NestFactory.create(AppModule);
6
7  // Basic Helmet configuration
8  app.use(helmet());
9
10  // Advanced Helmet configuration
11  app.use(helmet({
12    // Content-Security-Policy - controls where resources are loaded from
13    contentSecurityPolicy: {
14      directives: {
15        defaultSrc: ["'self'"],
16        scriptSrc: ["'self'", "'unsafe-inline'"],
17        styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
18        fontSrc: ["'self'", 'https://fonts.gstatic.com'],
19        imgSrc: ["'self'", 'data:', 'https:'],
20        connectSrc: ["'self'", process.env.API_URL],
21        frameSrc: ["'none'"],
22        objectSrc: ["'none'"],
23      },
24    },
25    // Strict-Transport-Security - enforces HTTPS
26    hsts: {
27      maxAge: 31536000, // 1 year
28      includeSubDomains: true,
29      preload: true,
30    },
31    // X-Frame-Options - prevents clickjacking
32    frameguard: { action: 'deny' },
33    // X-Content-Type-Options - prevents MIME sniffing
34    noSniff: true,
35    // Referrer-Policy
36    referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
37  }));
38}

CORS for production

CORS (Cross-Origin Resource Sharing) controls which domains can communicate with our API. It's like a list of trusted ambassadors of the Empire.

1// src/main.ts - production CORS configuration
2import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
3
4const corsOptions: CorsOptions = {
5  origin: (origin, callback) => {
6    const allowedOrigins = process.env.CORS_ORIGINS?.split(',') || [];
7
8    // Allow requests without origin (mobile, Postman)
9    if (!origin) return callback(null, true);
10
11    if (allowedOrigins.includes(origin)) {
12      callback(null, true);
13    } else {
14      callback(new Error(\`Origin \${origin} is not allowed by CORS\`));
15    }
16  },
17  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
18  allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
19  exposedHeaders: ['X-Total-Count', 'X-Page-Count'],
20  credentials: true,
21  maxAge: 86400, // Cache preflight for 24h
22};
23
24app.enableCors(corsOptions);

Rate Limiting with @nestjs/throttler

Rate limiting is like guards at the city gates - they control how many people can enter at a given time, preventing a siege (DDoS attacks).

1// src/app.module.ts - ThrottlerModule configuration
2import { Module } from '@nestjs/common';
3import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
4import { APP_GUARD } from '@nestjs/core';
5
6@Module({
7  imports: [
8    ThrottlerModule.forRoot([
9      {
10        name: 'short',  // Short-term limit
11        ttl: 1000,      // 1 second
12        limit: 3,       // Max 3 requests per second
13      },
14      {
15        name: 'medium', // Medium limit
16        ttl: 10000,     // 10 seconds
17        limit: 20,      // Max 20 requests per 10 seconds
18      },
19      {
20        name: 'long',   // Long-term limit
21        ttl: 60000,     // 1 minute
22        limit: 100,     // Max 100 requests per minute
23      },
24    ]),
25  ],
26  providers: [
27    {
28      provide: APP_GUARD,
29      useClass: ThrottlerGuard,
30    },
31  ],
32})
33export class AppModule {}
34
35// Disabling rate limiting for specific endpoints
36import { SkipThrottle, Throttle } from '@nestjs/throttler';
37
38@Controller('legions')
39export class LegionsController {
40  @SkipThrottle()  // No limit for health check
41  @Get('health')
42  healthCheck() {
43    return { status: 'ok' };
44  }
45
46  @Throttle({ short: { ttl: 1000, limit: 1 } }) // Stricter limit
47  @Post('login')
48  login() {
49    // Login with 1 req/s limitation
50  }
51}

CSRF Protection

CSRF (Cross-Site Request Forgery) is an attack where a malicious site makes requests on behalf of a logged-in user. We protect against it using CSRF tokens.

1// src/main.ts - CSRF protection with csurf
2import * as csurf from 'csurf';
3
4async function bootstrap() {
5  const app = await NestFactory.create(AppModule);
6
7  // CSRF protection (for applications with sessions/cookies)
8  app.use(csurf({
9    cookie: {
10      httpOnly: true,
11      secure: process.env.NODE_ENV === 'production',
12      sameSite: 'strict',
13    },
14  }));
15
16  // Middleware setting CSRF token in the response
17  app.use((req, res, next) => {
18    res.cookie('XSRF-TOKEN', req.csrfToken(), {
19      httpOnly: false, // Frontend must be able to read it
20      secure: process.env.NODE_ENV === 'production',
21      sameSite: 'strict',
22    });
23    next();
24  });
25}

Complete security configuration

1// src/security/security.module.ts
2import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
3import { ThrottlerModule } from '@nestjs/throttler';
4import helmet from 'helmet';
5
6@Module({
7  imports: [
8    ThrottlerModule.forRoot([
9      { name: 'short', ttl: 1000, limit: 3 },
10      { name: 'long', ttl: 60000, limit: 100 },
11    ]),
12  ],
13})
14export class SecurityModule implements NestModule {
15  configure(consumer: MiddlewareConsumer) {
16    consumer
17      .apply(
18        helmet(),
19        helmet.contentSecurityPolicy({
20          directives: {
21            defaultSrc: ["'self'"],
22            scriptSrc: ["'self'"],
23          },
24        }),
25      )
26      .forRoutes('*');
27  }
28}

Security is not a one-time task but a continuous process - just as defending the Empire's walls required constant vigilance from the guards! Each layer of security (SSL, Helmet, CORS, Rate Limiting, CSRF) adds another defensive wall around your application.

Go to CodeWorlds