Volver a explorar Snippets

Middleware de Tarpitting Avanzado: Mitigación Dinámica de Bots en Express.js con Respuestas de Éxito Falsas

JAVASCRIPT 18 de junio de 2026 1 lecturas
Implemento un middleware de tarpitting avanzado para Express.js que engaña a bots maliciosos con retrasos asíncronos y respuestas de éxito falsas, protegiendo recursos críticos sin revelar nuestra estrategia de defensa.

En mi experiencia como desarrollador, he observado que cuando un endpoint público —ya sea un formulario de contacto, una ruta de login o cualquier API expuesta— es blanco de ataques automatizados por bots, la respuesta convencional de bloquearlos con códigos HTTP 429 (Too Many Requests) o 403 (Forbidden) a menudo resulta contraproducente. Estos códigos alertan al atacante sobre su detección, lo que les permite adaptar rápidamente sus scripts, rotar direcciones IP o modificar cabeceras para evadir las defensas.

La Estrategia del Tarpit: Atrapando Bots en el Alquitrán Digital

Para contrarrestar esta sofisticación, implemento la técnica de Tarpitting, una estrategia que redefine la seguridad perimetral. En lugar de simplemente rechazar una petición con un error o, peor aún, procesarla y consumir valiosos recursos de la base de datos, mi servidor introduce un retraso artificial y asíncrono en la respuesta HTTP. Desde la perspectiva del bot, el servidor parece inexplicablemente lento, lo que efectivamente congela sus hilos de ejecución y ralentiza drásticamente su capacidad de ataque. Lo más crucial es que, para mi backend, el costo computacional de esta operación es prácticamente nulo, gracias a la naturaleza no bloqueante del bucle de eventos de Node.js.

Inyección de Falsos Éxitos: El Engaño del 200 OK

Mi enfoque va un paso más allá al combinar este retraso estratégico con un retorno simulado de éxito. Al recibir un código HTTP 200 OK, el atacante asume que su payload ha sido procesado correctamente y, por lo tanto, detiene su ráfaga de peticiones. Esta técnica no solo mitiga el ataque de manera eficiente, sino que también garantiza que las tablas de mi base de datos permanezcan completamente limpias y optimizadas, sin la necesidad de procesar o almacenar datos maliciosos.

const express = require('express');
const app = express();
const { v4: uuidv4 } = require('uuid'); // Necesitarás instalar 'uuid': npm install uuid

// Si tu aplicación se ejecuta detrás de un proxy (ej. Nginx, Heroku, AWS ELB),
// es crucial habilitar 'trust proxy' para que req.ip sea preciso.
// De lo contrario, req.ip siempre mostrará la IP del proxy.
app.set('trust proxy', true);

app.use(express.json());

// --- IMPLEMENTACIÓN DEL MIDDLEWARE DE TARPITTING ---

/**
 * Simula un retraso asíncrono utilizando el bucle de eventos de Node.js.
 * @param {number} ms - Milisegundos de retraso.
 * @returns {Promise<void>} Una promesa que se resuelve después del retraso.
 */
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

/**
 * Opciones de configuración para el middleware de Tarpitting.
 * @typedef {object} TarpitOptions
 * @property {number} baseDelayMs - Retraso base en milisegundos para el tarpit.
 * @property {number} criticalScore - Puntuación mínima de confianza para activar el tarpit (0.0 a 1.0).
 * @property {number} maxDelayMs - Retraso máximo en milisegundos, útil para evitar retrasos excesivos en casos extremos.
 */

/**
 * Middleware para auditar el payload y aplicar retrasos controlados (Tarpit)
 * basado en un score de confianza.
 * @param {TarpitOptions} opciones - Opciones de configuración para el middleware.
 * @returns {import('express').RequestHandler} El middleware de Express.
 */
const intelligentTarpitMiddleware = (opciones = {}) => {
    const {
        baseDelayMs = 3000, // 3 segundos por defecto
        criticalScore = 0.90, // 90% de confianza de ser spam
        maxDelayMs = 10000 // Retraso máximo de 10 segundos
    } = opciones;

    return async (req, res, next) => {
        const requestId = uuidv4(); // Genera un ID único para la petición
        // Para fines de logging o trazabilidad posterior
        req.requestId = requestId; 

        // En un entorno de producción real, 'spamConfidenceScore' provendría de:
        // 1. Un servicio de análisis externo (ej. Google reCAPTCHA Enterprise, AWS WAF, un modelo de IA como Gemini).
        // 2. Lógica interna de detección de anomalías (ej. rate limiting, análisis de patrones en cabeceras/payload).
        // Para este ejemplo, simulamos el score desde el cuerpo de la petición.
        const spamConfidenceScore = req.body.spamScore || 0.0;

        if (spamConfidenceScore >= criticalScore) {
            // Calcula un retraso dinámico: mayor score, mayor retraso, hasta un máximo.
            const calculatedDelay = Math.min(
                baseDelayMs + (spamConfidenceScore - criticalScore) * (maxDelayMs - baseDelayMs) / (1 - criticalScore),
                maxDelayMs
            );

            console.warn(
                `🚨 [Tarpit Activado - ID: ${requestId}] Petición sospechosa (Score: ${spamConfidenceScore.toFixed(2)}) ` +
                `desde IP: ${req.ip}. Aplicando retraso de ${calculatedDelay.toFixed(0)}ms.`
            );

            // Atrapamos al bot en el "alquitrán": retrasamos la respuesta asíncronamente.
            // Esto consume los recursos del cliente, no del servidor Express.
            await delay(calculatedDelay);

            // Respondemos con un FALSO 200 OK para engañar al script del bot.
            // Es crucial no llamar a next() aquí para evitar que la petición llegue al controlador real
            // y, por ende, a la base de datos o servicios downstream.
            return res.status(200).json({
                status: "success",
                message: "Su solicitud ha sido procesada.", // Mensaje genérico para evitar dar pistas
                refId: requestId,
                timestamp: new Date().toISOString()
            });
        }

        // Si la petición es legítima o no alcanza el score crítico, continúa limpiamente
        // hacia el siguiente middleware o controlador de ruta.
        next();
    };
};

// --- EJEMPLO DE APLICACIÓN EN RUTA CRÍTICA ---

// Middleware para simular la inyección de un score de spam para pruebas
// Puedes enviar { "spamScore": 0.95 } en el body o ?testSpamScore=0.95 en la URL
app.use('/api/v1/contacto', (req, res, next) => {
    if (req.query.testSpamScore) {
        req.body.spamScore = parseFloat(req.query.testSpamScore);
    }
    next();
});

app.post('/api/v1/contacto', intelligentTarpitMiddleware({
    baseDelayMs: 4000, // Retraso base de 4 segundos
    criticalScore: 0.85, // Activar a partir de 85% de sospecha
    maxDelayMs: 15000 // Retraso máximo de 15 segundos
}), (req, res) => {
    // Esta sección solo se ejecuta si el mensaje pasa el filtro perimetral.
    // Aquí iría la lógica de negocio real, como guardar en DB, enviar emails, etc.
    console.log(`[ID: ${req.requestId || 'N/A'}] 📦 Guardando mensaje legítimo en PostgreSQL a través de Prisma...`);

    res.status(201).json({
        status: "success",
        message: 'Mensaje real guardado en la base de datos.',
        data: req.body // Opcional: devolver el payload procesado
    });
});

// Ruta de ejemplo para mostrar que el tarpit no afecta rutas no protegidas
app.get('/api/v1/saludo', (req, res) => {
    res.status(200).json({ message: 'Hola, esta ruta no está protegida por tarpit.' });
});


const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Búnker de seguridad perimetral activo en puerto ${PORT}`));
¿Qué te pareció?
🔥 Brillante 0
💡 Me sirvió 0
🚀 A otro nivel 0