nestjs-security-module
Version:
A plug-and-play NestJS security module with CORS, Helmet, rate limiting, audit logging, CSP, XSS sanitization, and more.
293 lines (235 loc) β’ 10.3 kB
Markdown
# NestJS Security Module π
[](https://badge.fury.io/js/nestjs-security-module)
[]()
> A plug-and-play security module for NestJS, bundling best-practice HTTP headers, CORS, rate-limiting, audit logging, CSP, XSS sanitization and more.
## Table of Contents
1. [Features](#features)
2. [Installation](#installation)
3. [Basic Usage](#basic-usage)
4. [Async / Env-Based Configuration](#async--env-based-configuration)
5. [Options Reference](#options-reference)
6. [CORS Configuration](#cors-configuration)
7. [Example `.env`](#example-env)
8. [Troubleshooting](#troubleshooting)
## Features
- π **Helmet** integration for standard security headers
- π **Enhanced CORS** support with preflight request handling and case-sensitive headers
- π‘οΈ **Rate Limiting** (per-IP)
- π **Audit Logging** (to console + file)
- π **Content-Security-Policy** (CSP)
- π§Ή **XSS Sanitization** (deep sanitize middleware)
- βοΈ Additional headers: Referrer-Policy, HSTS, Expect-CT, Permissions-Policy, COEP β¦and more
## Installation
```bash
npm install nestjs-security-module
# or
yarn add nestjs-security-module
```
## Basic Usage
Import and configure the module in your `AppModule`:
```ts
// app.module.ts
import { Module } from '@nestjs/common';
import { SecurityModule } from 'nestjs-security-module';
@Module({
imports: [
SecurityModule.forRoot({
helmet: true,
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'HEAD', 'POST'],
allowedHeaders: ['Content-Type', 'content-type', 'Authorization', 'Accept', 'Origin', 'X-Requested-With'],
credentials: true,
},
rateLimit: { windowMs: 60_000, max: 10 },
auditLog: true,
csp: true,
sanitize: true,
referrerPolicy: true,
xFrameOptions: 'SAMEORIGIN',
hsts: true,
expectCt: true,
permissionsPolicy: { geolocation: ['self'] },
crossOriginEmbedderPolicy: true,
}),
// β¦ your other modules
],
})
export class AppModule {}
```
## Async / Env-Based Configuration
If you prefer loading options from environment variables via `@nestjs/config`, use the async API:
```ts
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { SecurityModule } from 'nestjs-security-module';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
SecurityModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (cfg: ConfigService) => ({
helmet: cfg.get<boolean>('SECURITY_HELMET'),
cors: cfg.get<boolean>('SECURITY_CORS')
? {
origin: cfg.get<string>('CORS_ORIGIN'),
methods: cfg.get<string>('CORS_METHODS').split(','),
allowedHeaders: ['Content-Type', 'content-type', 'Authorization', 'Accept', 'Origin', 'X-Requested-With'],
credentials: true,
}
: undefined,
rateLimit: cfg.get<boolean>('SECURITY_RATE_LIMIT')
? {
windowMs: cfg.get<number>('RATE_LIMIT_WINDOW'),
max: cfg.get<number>('RATE_LIMIT_MAX'),
}
: undefined,
auditLog: cfg.get<boolean>('SECURITY_AUDIT_LOG'),
csp: cfg.get<boolean>('SECURITY_CSP') ? { directives: { defaultSrc: ["'self'"] } } : undefined,
sanitize: cfg.get<boolean>('SECURITY_SANITIZE'),
referrerPolicy: cfg.get<boolean>('SECURITY_REFERRER'),
xFrameOptions: cfg.get<boolean>('SECURITY_XFRAME') ? 'SAMEORIGIN' : undefined,
hsts: cfg.get<boolean>('SECURITY_HSTS') ? { maxAge: parseInt(cfg.get<string>('SECURITY_HSTS_MAX_AGE')) } : undefined,
xContentTypeOptions: cfg.get<boolean>('SECURITY_XCONTENT_TYPE_OPTIONS'),
expectCt: cfg.get<boolean>('SECURITY_EXPECT_CT') ? { maxAge: parseInt(cfg.get<string>('SECURITY_EXPECT_CT_MAX_AGE')) } : undefined,
permissionsPolicy: cfg.get<boolean>('SECURITY_PERMISSIONS') ? { geolocation: ['self'] } : undefined,
crossOriginEmbedderPolicy: cfg.get<boolean>('SECURITY_COEP'),
}),
}),
],
})
export class AppModule {}
```
## Options Reference
| Option | Type | Description |
| --------------------------- | ------------------------------------------------ | ------------------------------------------- |
| `helmet` | `boolean` | Enable Helmet middleware |
| `cors` | `boolean \| CORSConfig` | Enable/customize CORS with enhanced support |
| `rateLimit` | `{ windowMs: number; max: number }` | IP-based rate limiting |
| `auditLog` | `boolean` | Log requests to console + file |
| `csp` | `boolean \| object` | Enable CSP (Content Security Policy) |
| `sanitize` | `boolean` | Deep sanitize incoming payloads |
| `referrerPolicy` | `boolean \| object` | Set Referrer-Policy header |
| `xFrameOptions` | `boolean \| 'DENY' \| 'SAMEORIGIN'` | Set X-Frame-Options header |
| `hsts` | `boolean \| object` | Enforce HTTPS via Strict-Transport-Security |
| `xContentTypeOptions` | `boolean` | Prevent MIME sniffing |
| `expectCt` | `boolean \| object` | Set Expect-CT header |
| `permissionsPolicy` | `boolean \| Record<string, string[]>` | Set Permissions-Policy header |
| `crossOriginEmbedderPolicy` | `boolean \| object` | Enable COEP header |
## CORS Configuration
The CORS configuration has been enhanced with the following improvements:
### Enhanced CORS Options
```ts
interface CORSConfig {
origin: string;
methods: string[] | string;
allowedHeaders?: string[];
credentials?: boolean;
}
```
### Key Improvements
- β
**Preflight Request Handling**: Automatic OPTIONS request handling
- β
**Case-Sensitive Headers**: Support for both `Content-Type` and `content-type`
- β
**Array/String Methods**: Support for both array and string method definitions
- β
**Credentials Support**: Proper handling of credentials in CORS requests
- β
**Max-Age Caching**: 24-hour preflight response caching
### Example CORS Configuration
```ts
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'HEAD', 'POST'], // Array format
// or methods: 'GET,HEAD,POST', // String format
allowedHeaders: ['Content-Type', 'content-type', 'Authorization', 'Accept', 'Origin', 'X-Requested-With'],
credentials: true,
}
```
### Manual OPTIONS Endpoint (Optional)
For additional control, you can add a manual OPTIONS endpoint:
```ts
// app.controller.ts
import { Controller, Options, Res } from '@nestjs/common';
import { Response } from 'express';
@Controller()
export class AppController {
@Options()
handleOptions(@Res() res: Response): void {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,POST,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type,content-type,Authorization,Accept,Origin,X-Requested-With');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Max-Age', '86400');
res.status(200).end();
}
}
```
## Example `.env`
```dotenv
SECURITY_HELMET=true
SECURITY_CORS=true
CORS_ORIGIN=http://localhost:3000
CORS_METHODS=GET,HEAD,POST
SECURITY_RATE_LIMIT=true
RATE_LIMIT_WINDOW=60000
RATE_LIMIT_MAX=10
SECURITY_AUDIT_LOG=true
SECURITY_CSP=true
SECURITY_SANITIZE=true
SECURITY_REFERRER=true
SECURITY_XFRAME=true
SECURITY_HSTS=true
SECURITY_HSTS_MAX_AGE=31536000
SECURITY_XCONTENT_TYPE_OPTIONS=true
SECURITY_EXPECT_CT=true
SECURITY_EXPECT_CT_MAX_AGE=30
SECURITY_PERMISSIONS=true
SECURITY_COEP=true
```
## Troubleshooting
### CORS Issues
If you encounter CORS errors, ensure:
1. **Origin is correctly set**: Use specific origin instead of wildcard `*`
2. **Headers are case-sensitive**: Include both `Content-Type` and `content-type`
3. **Methods are properly formatted**: Use array format for better compatibility
4. **Credentials are enabled**: Set `credentials: true` for authenticated requests
### Rate Limiting
- Rate limiting is per-IP address
- Default: 10 requests per 60 seconds
- Configure via `RATE_LIMIT_WINDOW` and `RATE_LIMIT_MAX` environment variables
### Security Headers
All security headers are automatically applied when enabled:
- `Content-Security-Policy`
- `Strict-Transport-Security`
- `X-Frame-Options`
- `X-Content-Type-Options`
- `Referrer-Policy`
- `Permissions-Policy`
- `Cross-Origin-Embedder-Policy`
## Testing
Test your CORS configuration:
```bash
# Test preflight request
curl -v -H "Origin: http://localhost:3000" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: content-type" \
-X OPTIONS http://localhost:3001
# Test regular request
curl -v -H "Origin: http://localhost:3000" \
-H "Content-Type: application/json" \
-X POST http://localhost:3001
```
## Contributing
This module includes enhanced CORS support and improved security features. For issues or contributions, please refer to the project repository.