Vulnerabilidad corregida: - Slowloris: un atacante podía abrir cientos de conexiones TCP enviando headers/body a goteo (1 byte cada varios segundos) manteniendo el event loop de Node.js ocupado con sockets colgados indefinidamente, lo que bloquea peticiones legítimas (DoS por agotamiento de sockets). Mitigación aplicada en el servidor HTTP: headersTimeout = 10s — aborta si los headers no llegan en 10 s requestTimeout = 15s — aborta si el body no llega en 15 s keepAliveTimeout = 5s — cierra keep-alive inactivos tras 5 s Adicionalmente: warning al arrancar si SALT no está configurado en .env. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
66 lines
2.6 KiB
JavaScript
66 lines
2.6 KiB
JavaScript
'use strict';
|
|
|
|
require('dotenv').config();
|
|
|
|
const express = require('express');
|
|
const helmet = require('helmet');
|
|
const rateLimit = require('express-rate-limit');
|
|
|
|
const eraseRoute = require('./routes/erase');
|
|
const gmailOAuth = require('./routes/gmail_oauth');
|
|
const egosearch = require('./routes/egosearch');
|
|
|
|
const app = express();
|
|
|
|
app.set('trust proxy', 1); // confía en nginx para obtener la IP real del cliente
|
|
app.disable('x-powered-by');
|
|
app.use(helmet());
|
|
app.use(express.json({ limit: '10kb' }));
|
|
|
|
// Rate limiting general
|
|
app.use(rateLimit({
|
|
windowMs: 15 * 60 * 1000,
|
|
max: 100,
|
|
standardHeaders: true,
|
|
legacyHeaders: false,
|
|
}));
|
|
|
|
// 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.' }
|
|
});
|
|
|
|
// ── Rate limits específicos ───────────────────────────────────────
|
|
const searchLimit = rateLimit({
|
|
windowMs: 60 * 1000, // 1 minuto
|
|
max: 8,
|
|
message: { error: 'Demasiadas búsquedas. Espera un momento.' }
|
|
});
|
|
|
|
// ── Rutas ────────────────────────────────────────────────────────
|
|
app.post('/api/erase', emailLimit, eraseRoute);
|
|
app.get('/api/egosearch', searchLimit, egosearch);
|
|
app.get('/api/gmail/auth', emailLimit, gmailOAuth.authInit);
|
|
app.get('/api/gmail/callback', gmailOAuth.authCallback);
|
|
|
|
// Health check
|
|
app.get('/api/health', (req, res) => res.json({ status: 'ok' }));
|
|
|
|
// ── Arranque ─────────────────────────────────────────────────────
|
|
const PORT = process.env.PORT || 8787;
|
|
const server = app.listen(PORT, '127.0.0.1', () => {
|
|
console.log(`RESETEA backend corriendo en 127.0.0.1:${PORT}`);
|
|
if (!process.env.GOOGLE_CLIENT_ID) {
|
|
console.warn('⚠ GOOGLE_CLIENT_ID no configurado — OAuth Gmail deshabilitado');
|
|
}
|
|
if (!process.env.SALT) {
|
|
console.warn('⚠ SALT no configurado en .env — el hash de referencia usa salt por defecto');
|
|
}
|
|
});
|
|
|
|
// Mitigación Slowloris: timeout en headers y body incompletos
|
|
server.headersTimeout = 10_000; // 10 s para recibir los headers completos
|
|
server.requestTimeout = 15_000; // 15 s para recibir el body completo
|
|
server.keepAliveTimeout = 5_000; // cierra keep-alive inactivos tras 5 s
|