miércoles, 25 de junio de 2025

Implementación de Rate Limiting Distribuido con Redis y PHP

Implementación de Rate Limiting Distribuido con Redis y PHP

El rate limiting, o limitación de velocidad, es una técnica crucial para proteger APIs y servicios web contra abusos, ataques DDoS y el consumo excesivo de recursos. Implementar un rate limiter robusto y escalable requiere una solución distribuida, especialmente en entornos con múltiples servidores. Este artículo explora cómo implementar un rate limiter distribuido utilizando Redis y PHP.

Redis, una base de datos en memoria, ofrece las características necesarias para una implementación eficiente de rate limiting distribuido, incluyendo operaciones atómicas y un rendimiento extremadamente rápido. Utilizaremos listas de Redis para rastrear las solicitudes dentro de un período de tiempo específico para cada usuario o IP.


<?php

// Configuración de Redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

/**
 * Aplica rate limiting a una clave específica (por ejemplo, IP o ID de usuario).
 *
 * @param string $key La clave a la que se aplicará el rate limiting.
 * @param int $limit El número máximo de solicitudes permitidas.
 * @param int $periodo El período de tiempo en segundos dentro del cual se aplica el límite.
 * @return bool True si la solicitud está permitida, False si se excede el límite.
 */
function aplicarRateLimit(string $key, int $limit, int $periodo): bool {
    global $redis;

    $claveRedis = "rate_limit:" . $key;
    $timestampActual = time();

    // Elimina las solicitudes antiguas de la lista
    $redis->zRemRangeByScore($claveRedis, 0, $timestampActual - $periodo);

    // Obtiene el número de solicitudes restantes
    $conteoSolicitudes = $redis->zCard($claveRedis);

    // Verifica si se ha excedido el límite
    if ($conteoSolicitudes >= $limit) {
        return false; // Límite excedido
    }

    // Agrega la solicitud actual a la lista
    $redis->zAdd($claveRedis, $timestampActual, $timestampActual);

    // Configura un tiempo de vida para la clave (opcional, para limpieza)
    $redis->expire($claveRedis, $periodo * 2);

    return true; // Solicitud permitida
}

// Ejemplo de uso: Limitar las solicitudes de una IP a 10 por minuto
$ipUsuario = $_SERVER['REMOTE_ADDR'];
$limite = 10;
$periodo = 60; // 60 segundos (1 minuto)

if (aplicarRateLimit($ipUsuario, $limite, $periodo)) {
    // Procesar la solicitud
    echo "Solicitud permitida.";
} else {
    // Rechazar la solicitud
    http_response_code(429); // Too Many Requests
    echo "Demasiadas solicitudes. Inténtalo de nuevo más tarde.";
}

$redis->close();

Este código utiliza sorted sets de Redis para almacenar las marcas de tiempo de las solicitudes. La función `zRemRangeByScore` elimina las solicitudes antiguas, `zCard` cuenta las solicitudes actuales y `zAdd` agrega una nueva solicitud. Al usar sorted sets, podemos eficientemente contar y eliminar solicitudes basándonos en su marca de tiempo. La función `aplicarRateLimit` devuelve `true` si la solicitud está permitida y `false` en caso contrario, lo que permite rechazar solicitudes y devolver un código de estado HTTP 429 (Too Many Requests).

Recuerda ajustar los parámetros de configuración de Redis (`host`, `port`) según tu entorno. Además, considera implementar un middleware o un interceptor en tu framework PHP para aplicar el rate limiting de forma centralizada.

No hay comentarios:

Publicar un comentario