Chuyển đến nội dung chính

Bài 11: Security Best Practices

Helmet, CORS configuration, Rate Limiting với @nestjs/throttler, CSRF protection, Input sanitization. Security headers, HTTPS, Environment variables với @nestjs/config.

💻 Lập trình — Bài 11 Bài 11: Security Best Practices

NestJS: Từ Cơ bản đến Nâng cao

Phần 3: Authentication & Security

xdev.asia

1. Helmet — Security Headers

npm install helmet
// main.ts
import helmet from 'helmet';

const app = await NestFactory.create(AppModule);
app.use(helmet());
// Tự động thêm headers:
// X-Content-Type-Options: nosniff
// X-Frame-Options: SAMEORIGIN
// X-XSS-Protection: 0
// Strict-Transport-Security: max-age=15552000
// Content-Security-Policy: default-src 'self'

2. CORS Configuration

const app = await NestFactory.create(AppModule);
app.enableCors({
  origin: [
    'https://myapp.com',
    'https://admin.myapp.com',
  ],
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true,
  maxAge: 3600,
});

3. Rate Limiting với @nestjs/throttler

npm install @nestjs/throttler
// app.module.ts
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';

@Module({
  imports: [
    ThrottlerModule.forRoot([
      { name: 'short', ttl: 1000, limit: 3 },   // 3 req/giây
      { name: 'medium', ttl: 10000, limit: 20 }, // 20 req/10 giây
      { name: 'long', ttl: 60000, limit: 100 },  // 100 req/phút
    ]),
  ],
  providers: [
    { provide: APP_GUARD, useClass: ThrottlerGuard }, // Global
  ],
})
export class AppModule {}

// Custom limits per route
@Throttle([{ name: 'short', ttl: 1000, limit: 1 }])  // 1 req/giây
@Post('login')
login() { ... }

// Skip throttling
@SkipThrottle()
@Get('health')
health() { return 'OK'; }

4. Environment Variables với @nestjs/config

npm install @nestjs/config joi
// app.module.ts
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as Joi from 'joi';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
      validationSchema: Joi.object({
        NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'),
        PORT: Joi.number().default(3000),
        DATABASE_URL: Joi.string().required(),
        JWT_ACCESS_SECRET: Joi.string().required().min(32),
        JWT_REFRESH_SECRET: Joi.string().required().min(32),
        REDIS_URL: Joi.string().optional(),
      }),
    }),
  ],
})
export class AppModule {}

// Sử dụng
@Injectable()
export class AuthService {
  constructor(private config: ConfigService) {}

  getJwtSecret(): string {
    return this.config.get<string>('JWT_ACCESS_SECRET');
  }
}

5. Input Sanitization

// Dùng class-transformer để sanitize
import { Transform } from 'class-transformer';

export class CreateCommentDto {
  @IsString()
  @Transform(({ value }) => value.replace(/<[^>]*>/g, ''))  // Strip HTML tags
  content: string;

  @IsString()
  @Transform(({ value }) => value.trim().toLowerCase())
  email: string;
}

// Hoặc dùng DOMPurify/sanitize-html cho HTML content
import sanitizeHtml from 'sanitize-html';

@Transform(({ value }) => sanitizeHtml(value, {
  allowedTags: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
  allowedAttributes: { a: ['href'] },
}))
htmlContent: string;

6. Phòng chống SQL Injection

// ✅ Đúng — Parameterized queries (TypeORM)
const users = await repo.createQueryBuilder('user')
  .where('user.email = :email', { email: userInput })
  .getMany();

// ✅ Đúng — Prisma (tự động parameterized)
const users = await prisma.user.findMany({
  where: { email: userInput },
});

// ❌ SAI — String concatenation
const users = await repo.query(`SELECT * FROM users WHERE email = '${userInput}'`);

7. Các Best Practices khác

// 1. Compression
import compression from 'compression';
app.use(compression());

// 2. Request size limit
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ limit: '10mb', extended: true }));

// 3. Logging — không log sensitive data
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    const req = context.switchToHttp().getRequest();
    const { password, token, ...safeBody } = req.body;
    console.log(`${req.method} ${req.url}`, safeBody);
    return next.handle();
  }
}

// 4. Graceful shutdown
app.enableShutdownHooks();

8. Tổng kết

Mối đe dọaGiải pháp
XSSHelmet, Input sanitization, CSP headers
SQL InjectionParameterized queries (TypeORM/Prisma)
Brute ForceRate Limiting (@nestjs/throttler)
CSRFSameSite cookies, CSRF tokens
Sensitive Data@nestjs/config, .env files, không log passwords
ClickjackingX-Frame-Options via Helmet
CORSWhitelist origins

Bài tiếp theo sẽ tìm hiểu Session, Cookies và OAuth2.