Rate Limiting en .NET: Protegiendo tu API desde el Principio

En el mundo del desarrollo de software moderno, la seguridad y el rendimiento no deben ser una ocurrencia tardía, sino pilares fundamentales desde el inicio del proyecto. Ignorar estos aspectos puede llevar a vulnerabilidades críticas y a un deterioro del rendimiento que afecte directamente la experiencia del usuario y, en última instancia, la viabilidad de un producto. Una de las estrategias más efectivas y sencillas para mitigar estos riesgos es la implementación de Rate Limiting. image

Este artículo es el primero de una serie enfocada en la seguridad, donde exploraremos diversas acciones, tanto a nivel de código como de infraestructura, para proteger nuestras aplicaciones.


Qué es el Rate Limiting y por qué es tan importante? 🔐

El Rate Limiting, o limitación de tasa, es una práctica de seguridad que controla la cantidad de solicitudes que un cliente puede hacer a un servidor dentro de un período de tiempo determinado.

La analogía de un fusible en el hogar es perfecta: cuando el consumo de energía excede un límite predefinido, el fusible salta para evitar daños mayores. En el software, el Rate Limiting actúa de manera similar, protegiendo nuestros recursos de un uso excesivo.

¿Por qué es crucial para una API o servicio web?

Es importante recordar que el Rate Limiting no es una solución de seguridad única. Es parte de una estrategia de defensa en profundidad que debe complementarse con otras medidas, como el uso de un WAF (Web Application Firewall), bloqueo de IPs maliciosas y detección de bots.


Implementando Rate Limiting en .NET 🚀

Desde .NET 8, el middleware de Rate Limiting es una característica integrada en el framework, lo que simplifica enormemente su implementación. Para versiones anteriores, puedes utilizar el paquete NuGet Microsoft.AspNetCore.RateLimiting.

La configuración se realiza en el archivo Program.cs de tu proyecto. El primer paso es añadir el servicio:

builder.Services.AddRateLimiter(options =>
{
    // Aquí se definen las políticas de limitación
});

El objeto options de RateLimiterOptionsExtensions nos permite definir varias políticas de limitación.

Tipos de Políticas de Rate Limiting en .NET

El framework ofrece cuatro algoritmos de control principales:

Configuración de las Políticas

Aquí tienes un ejemplo de cómo configurar cada una de estas políticas en tu código:

builder.Services.AddRateLimiter(options =>
{
    // Política de Ventana Fija
    options.AddFixedWindowLimiter("FixedPolicy", opt =>
    {
        opt.Window = TimeSpan.FromSeconds(20);
        opt.PermitLimit = 2;
        opt.QueueLimit = 0;
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
    });

    // Política de Ventana Deslizante
    options.AddSlidingWindowLimiter("SlidingPolicy", opt =>
    {
        opt.Window = TimeSpan.FromSeconds(20);
        opt.SegmentsPerWindow = 2;
        opt.PermitLimit = 2;
        opt.QueueLimit = 0;
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
    });

    // Política de Cubo de Tokens
    options.AddTokenBucketLimiter("TokenPolicy", opt =>
    {
        opt.TokenLimit = 2;
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        opt.QueueLimit = 0;
        opt.ReplenishmentPeriod = TimeSpan.FromSeconds(20);
        opt.TokensPerPeriod = 60;
        opt.AutoReplenishment = true;
    });
    
    // Política de Concurrencia
    options.AddConcurrencyLimiter("ConcurrencyPolicy", opt =>
    {
        opt.PermitLimit = 10;
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
        opt.QueueLimit = 2;
    });

    // Configuración global para rechazos
    options.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
});

Una de las decisiones más importantes es la configuración de la cola de espera (QueueLimit y QueueProcessingOrder). Si bien una cola puede manejar ráfagas de tráfico, también consume recursos de memoria y puede causar latencia en las respuestas. En muchos casos, rechazar la solicitud de inmediato (QueueLimit = 0) es preferible para mantener la estabilidad y evitar que un ataque DoS consuma todos los recursos.

Por defecto, si una solicitud es rechazada, el middleware devuelve un código de estado 503 (Service Unavailable). Sin embargo, en un contexto de API, es más apropiado devolver un 429 (Too Many Requests) para informar al cliente que ha excedido el límite de llamadas, lo cual se configura con options.RejectionStatusCode.


Aplicando las Políticas a los Endpoints

Una vez que las políticas están definidas, solo queda aplicarlas a los endpoints. En una API Minimal, esto se hace fácilmente con el método RequireRateLimiting().

    protected override RouteHandlerBuilder Configure(RouteHandlerBuilder builder)
        => builder
                // ... otras configuraciones
                .WithName("GetAvenger")
                .WithTags("Avengers")
                .RequireRateLimiting("FixedPolicy");

Aquí, hemos aplicado la política FixedPolicy al endpoint GetAvenger.


Resumen

El Rate Limiting es una medida de seguridad fundamental para proteger las aplicaciones web de un uso excesivo y malicioso. Al implementar esta técnica, prevenimos ataques de denegación de servicio (DoS), controlamos los costos de nuestra infraestructura y garantizamos la estabilidad del servicio. En .NET 8, el middleware integrado facilita su configuración, permitiendo definir diferentes políticas (Fixed, Sliding, Token Bucket y Concurrency) y aplicarlas de manera granular a los endpoints. Esta práctica, combinada con otras medidas de seguridad, es clave para construir productos digitales robustos y rentables.

Podéis ver el ejemplo en mi repositorio de Github

Happy Codding

Referencias

Rate limiting middleware in ASP.NET Core