feat(security): add per-endpoint API rate limiting with Redis sliding window
Implement @EndpointRateLimit() decorator and EndpointRateLimitGuard for granular per-endpoint rate limiting using a Redis sorted-set sliding window. This prevents brute force attacks on auth endpoints, replay attacks on payment callbacks, and scraping on search endpoints. Applied rate limits: - /auth/login: 5 req/min per IP - /auth/register: 3 req/min per IP - /listings POST: 10 req/min per user - /search: 30 req/min per user - /payments/callback/*: 100 req/min per IP Features: - True sliding window (sorted set) for accurate rate measurement - Configurable key strategy (IP or authenticated user) - Admin bypass support (enabled by default) - Fail-open on Redis errors - Proper 429 response with Retry-After header - Rate limit headers (X-RateLimit-Limit/Remaining/Reset) - 22 unit tests covering all scenarios Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -12,7 +12,7 @@ import { type CommandBus, type QueryBus } from '@nestjs/cqrs';
|
||||
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth, ApiBody } from '@nestjs/swagger';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
import { type Request, type Response } from 'express';
|
||||
import { UnauthorizedException } from '@modules/shared';
|
||||
import { EndpointRateLimit, EndpointRateLimitGuard, UnauthorizedException } from '@modules/shared';
|
||||
import { LoginUserCommand } from '../../application/commands/login-user/login-user.command';
|
||||
import { RefreshTokenCommand } from '../../application/commands/refresh-token/refresh-token.command';
|
||||
import { RegisterUserCommand } from '../../application/commands/register-user/register-user.command';
|
||||
@@ -79,6 +79,8 @@ export class AuthController {
|
||||
) {}
|
||||
|
||||
@Throttle({ default: { ttl: 3_600_000, limit: AUTH_RATE_LIMIT }, auth: { ttl: 3_600_000, limit: AUTH_RATE_LIMIT } })
|
||||
@EndpointRateLimit({ limit: IS_TEST ? 10_000 : 3, windowSeconds: 60, keyStrategy: 'ip' })
|
||||
@UseGuards(EndpointRateLimitGuard)
|
||||
@Post('register')
|
||||
@ApiOperation({ summary: 'Register a new user' })
|
||||
@ApiResponse({ status: 201, description: 'User registered, auth cookies set' })
|
||||
@@ -100,7 +102,8 @@ export class AuthController {
|
||||
}
|
||||
|
||||
@Throttle({ default: { ttl: 3_600_000, limit: AUTH_RATE_LIMIT }, auth: { ttl: 3_600_000, limit: AUTH_RATE_LIMIT } })
|
||||
@UseGuards(LocalAuthGuard)
|
||||
@EndpointRateLimit({ limit: IS_TEST ? 10_000 : 5, windowSeconds: 60, keyStrategy: 'ip' })
|
||||
@UseGuards(EndpointRateLimitGuard, LocalAuthGuard)
|
||||
@Post('login')
|
||||
@ApiOperation({ summary: 'Login with phone and password' })
|
||||
@ApiBody({ type: LoginDto })
|
||||
|
||||
Reference in New Issue
Block a user