fix(security): bypass de rate limiting via X-Forwarded-For spoofing — VULN: Rate limit evasion
Vulnerabilidad corregida: - Con 'trust proxy 1', Express usaba X-Forwarded-For como req.ip. Un atacante podia cambiar ese header en cada peticion para obtener un bucket de rate limit nuevo, bypasseando el limite de 8 busquedas/min. Demostrado: 12 requests exitosas cuando el limite era 8. Mitigacion: - keyGenerator en todos los rate limiters usa X-Real-IP (establecido por Nginx con $remote_addr, no manipulable por el cliente). - trust proxy cambiado de '1' a 'loopback'. - Verificado: con X-Real-IP fijo y XFF variando, rate limit actua en req 9. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f2ff04ecdc
commit
93d75ddafe
1 changed files with 16 additions and 4 deletions
20
api/app.js
20
api/app.js
|
|
@ -12,31 +12,43 @@ const egosearch = require('./routes/egosearch');
|
|||
|
||||
const app = express();
|
||||
|
||||
app.set('trust proxy', 1); // confía en nginx para obtener la IP real del cliente
|
||||
// trust proxy: loopback para que req.ip use X-Forwarded-For correctamente,
|
||||
// pero el keyGenerator de rate-limit usa X-Real-IP (imposible de spoofear por clientes)
|
||||
// porque Nginx lo establece desde $remote_addr (dirección TCP real).
|
||||
app.set('trust proxy', 'loopback');
|
||||
app.disable('x-powered-by');
|
||||
app.use(helmet());
|
||||
app.use(express.json({ limit: '10kb' }));
|
||||
|
||||
// Rate limiting general
|
||||
/* Extrae la IP real desde X-Real-IP (puesto por Nginx con $remote_addr).
|
||||
No puede ser manipulado por el cliente — Nginx sobreescribe este header. */
|
||||
function realIp(req) {
|
||||
return req.headers['x-real-ip'] || req.ip || '0.0.0.0';
|
||||
}
|
||||
|
||||
// Rate limiting general — usa IP real (anti-bypass X-Forwarded-For)
|
||||
app.use(rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 100,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
keyGenerator: realIp,
|
||||
}));
|
||||
|
||||
// Rate limiting específico para envío de emails (más estricto)
|
||||
const emailLimit = rateLimit({
|
||||
windowMs: 60 * 60 * 1000, // 1 hora
|
||||
max: 10,
|
||||
message: { error: 'Demasiadas solicitudes de envío. Espera antes de reintentar.' }
|
||||
message: { error: 'Demasiadas solicitudes de envío. Espera antes de reintentar.' },
|
||||
keyGenerator: realIp,
|
||||
});
|
||||
|
||||
// ── Rate limits específicos ───────────────────────────────────────
|
||||
const searchLimit = rateLimit({
|
||||
windowMs: 60 * 1000, // 1 minuto
|
||||
max: 8,
|
||||
message: { error: 'Demasiadas búsquedas. Espera un momento.' }
|
||||
message: { error: 'Demasiadas búsquedas. Espera un momento.' },
|
||||
keyGenerator: realIp,
|
||||
});
|
||||
|
||||
// ── Rutas ────────────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue