codigo0/backend/src/middleware/rate-limit.ts

75 lines
2.1 KiB
TypeScript

/**
* Rate Limiting Middleware
* Protege endpoints contra abuso y ataques de fuerza bruta
*/
import rateLimit, { RateLimitRequestHandler } from 'express-rate-limit';
/**
* Rate limiter general para toda la API
* 100 requests por 15 minutos por IP
*/
export const generalLimiter: RateLimitRequestHandler = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100, // 100 requests por IP
message: {
error: 'Demasiadas peticiones desde esta IP, intenta en 15 minutos',
retryAfter: 900, // segundos
},
standardHeaders: true, // Retorna rate limit info en headers `RateLimit-*`
legacyHeaders: false, // No usa `X-RateLimit-*` headers
skip: (req) => {
// Saltar rate limiting para health checks
return req.path === '/health';
},
});
/**
* Rate limiter estricto para endpoints de autenticación
* 5 intentos por 15 minutos por IP
* Previene ataques de fuerza bruta
*/
export const authLimiter: RateLimitRequestHandler = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 5, // Solo 5 intentos de login por IP
message: {
error: 'Demasiados intentos de login, intenta en 15 minutos',
retryAfter: 900,
},
standardHeaders: true,
legacyHeaders: false,
skipSuccessfulRequests: true, // No contar requests exitosos
skipFailedRequests: false, // Contar solo los fallidos
});
/**
* Rate limiter medio para creación de contenido
* 20 creaciones por hora por IP
*/
export const contentWriteLimiter: RateLimitRequestHandler = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hora
max: 20, // 20 creaciones por hora
message: {
error: 'Límite de creación de contenido alcanzado, intenta en 1 hora',
retryAfter: 3600,
},
standardHeaders: true,
legacyHeaders: false,
});
/**
* Rate limiter para endpoints de validación
* 50 validaciones por hora por IP
*/
export const validationLimiter: RateLimitRequestHandler = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hora
max: 50, // 50 validaciones por hora
message: {
error: 'Límite de validaciones alcanzado, intenta en 1 hora',
retryAfter: 3600,
},
standardHeaders: true,
legacyHeaders: false,
});