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:
@@ -13,6 +13,7 @@ import {
|
||||
ApiBearerAuth,
|
||||
} from '@nestjs/swagger';
|
||||
import { Roles, JwtAuthGuard, RolesGuard } from '@modules/auth';
|
||||
import { EndpointRateLimit, EndpointRateLimitGuard } from '@modules/shared';
|
||||
import { ReindexAllCommand } from '../../application/commands/reindex-all/reindex-all.command';
|
||||
import { type ReindexResult } from '../../application/commands/reindex-all/reindex-all.handler';
|
||||
import { GeoSearchQuery } from '../../application/queries/geo-search/geo-search.query';
|
||||
@@ -29,6 +30,8 @@ export class SearchController {
|
||||
private readonly queryBus: QueryBus,
|
||||
) {}
|
||||
|
||||
@EndpointRateLimit({ limit: 30, windowSeconds: 60, keyStrategy: 'user' })
|
||||
@UseGuards(EndpointRateLimitGuard)
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Search properties', description: 'Public full-text and faceted property search' })
|
||||
@ApiResponse({ status: 200, description: 'Search results returned successfully' })
|
||||
@@ -52,6 +55,8 @@ export class SearchController {
|
||||
);
|
||||
}
|
||||
|
||||
@EndpointRateLimit({ limit: 30, windowSeconds: 60, keyStrategy: 'user' })
|
||||
@UseGuards(EndpointRateLimitGuard)
|
||||
@Get('geo')
|
||||
@ApiOperation({ summary: 'Geo search properties', description: 'Public geographic radius property search' })
|
||||
@ApiResponse({ status: 200, description: 'Geo search results returned successfully' })
|
||||
|
||||
Reference in New Issue
Block a user