NestJSMiddleTechnical

Как реализовать rate limiting и заголовки безопасности в NestJS?

Rate limiting реализуется через @nestjs/throttler (декоратор @Throttle + ThrottlerGuard), заголовки безопасности — через пакет helmet подключённый как middleware. Оба механизма настраиваются глобально в AppModule.

Rate Limiting в NestJS

Официальный пакет @nestjs/throttler предоставляет guard и модуль для ограничения частоты запросов. Начиная с версии 5.x, модуль поддерживает несколько «throttler-ов» одновременно (например, короткое и длинное окно).

Установка

npm install @nestjs/throttler
npm install helmet

Конфигурация ThrottlerModule

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

@Module({
  imports: [
    ThrottlerModule.forRoot([
      {
        name: 'short',   // 10 запросов за 1 секунду
        ttl: 1000,
        limit: 10,
      },
      {
        name: 'long',    // 100 запросов за минуту
        ttl: 60000,
        limit: 100,
      },
    ]),
  ],
  providers: [
    {
      provide: APP_GUARD,
      useClass: ThrottlerGuard,  // глобальный rate limit
    },
  ],
})
export class AppModule {}

Тонкая настройка на уровне маршрута

import { Throttle, SkipThrottle } from '@nestjs/throttler';

@Controller('auth')
export class AuthController {
  // Жёсткий лимит для логина: 5 попыток за 60 секунд
  @Throttle({ default: { ttl: 60000, limit: 5 } })
  @Post('login')
  login(@Body() dto: LoginDto) { ... }

  // Публичный endpoint — пропускаем throttler
  @SkipThrottle()
  @Get('health')
  health() { return 'ok'; }
}

Rate limiting с Redis (для многоподового деплоя)

По умолчанию счётчики хранятся в памяти. В многоподовом окружении нужен Redis-хранилище:

import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis';

ThrottlerModule.forRootAsync({
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    throttlers: [{ ttl: 60000, limit: 100 }],
    storage: new ThrottlerStorageRedisService({
      host: config.get('REDIS_HOST'),
      port: config.get('REDIS_PORT'),
    }),
  }),
})

Заголовки безопасности с Helmet

Helmet устанавливает набор HTTP-заголовков, снижающих риск распространённых атак (XSS, clickjacking, MIME-sniffing и др.):

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

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

  // Helmet — подключать ДО всех маршрутов
  app.use(helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        styleSrc: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", 'data:', 'https:'],
        scriptSrc: ["'self'"],
      },
    },
    crossOriginEmbedderPolicy: false,  // отключить если нужен <iframe>
  }));

  // CORS
  app.enableCors({
    origin: process.env.ALLOWED_ORIGINS?.split(',') ?? 'http://localhost:3000',
    credentials: true,
    methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
  });

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

Что устанавливает Helmet по умолчанию

  • X-Content-Type-Options: nosniff — блокирует MIME-sniffing
  • X-Frame-Options: SAMEORIGIN — защита от clickjacking
  • Strict-Transport-Security — принудительный HTTPS (HSTS)
  • X-XSS-Protection: 0 — отключает устаревший XSS-фильтр браузера
  • Referrer-Policy: no-referrer — не передавать Referrer на другие домены
  • Content-Security-Policy — базовая политика (настраивается вручную)

Подводные камни

  • Throttler по умолчанию идентифицирует клиента по IP. За reverse-proxy нужно настроить getTracker на чтение заголовка X-Forwarded-For, иначе все пользователи будут идентифицированы как один IP прокси.
  • ThrottlerGuard возвращает 429 Too Many Requests — убедитесь, что ваш frontend обрабатывает этот статус и не показывает пользователю пустой экран.
  • Helmet нужно подключать через app.use(), а не через @UseInterceptors или guard — он должен быть Express/Fastify middleware.
  • При использовании Fastify вместо Express: helmet не подойдёт, нужен @fastify/helmet с app.register().
  • CSP-директива unsafe-inline для скриптов нивелирует защиту от XSS — используйте nonce или hash вместо неё.
  • HSTS (Strict-Transport-Security) в разработке через HTTP сломает локальный доступ — отключайте helmet или конкретную директиву в dev-окружении.
  • Глобальный ThrottlerGuard применяется и к WebSocket-шлюзам — убедитесь, что лимиты не слишком строгие для realtime-соединений.

Common mistakes

  • Дает общий ответ про Node.js и не называет конкретные API NestJS.
  • Не объясняет, где в lifecycle находится rate limiting и security headers.
  • Не разделяет validation, authorization, business logic и persistence.
  • Игнорирует ошибки, лимиты входных данных, observability и тестирование.

What the interviewer is testing

  • Может объяснить rate limiting и security headers на примере кода.
  • Называет ключевые API: ThrottlerModule, helmet().
  • Использует точные API NestJS, а не вымышленные hooks/decorators/methods.
  • Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.

Sources

Related topics