Control de Flujo de Solicitudes en .NET 6 con AspNetCoreRateLimit

Introducción a la Limitación de Tasa en Aplicaciones Web

La limitación de tasa (rate limiting) es una técnica esencial para proteger las API web y los servicios de abusos, sobrecargas o ataques de denegación de servicio (DoS). Al restringir el número de solicitudes que un cliente puede realizar dentro de un período de tiempo determinado, se asegura la disponibilidad y estabilidad del servicio para todos los ussuarios. En el ecosistema de .NET Core, la biblioteca AspNetCoreRateLimit ofrece una solución robusta y flexible para implementar esta funcionalidad.

Este artículo detalla cómo configurar y utilizar AspNetCoreRateLimit en un proyecto .NET 6 para establecer límites de solicitud basados en la dirección IP del cliente, ofreciendo un control granular sobre el acceso a los recursos de su API.

  1. Instalación del Paquete NuGet

Para comenzar, añada el paquete AspNetCoreRateLimit a su proyecto .NET 6. Esto se puede hacer a través del Administrador de Paquetes NuGet de Visual Studio, la CLI de .NET o editando directamente el archivo .csproj:

<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />

Este paquete provee la infraestructura necesaria para implementar políticas de limitación de tasa.

  1. Configuración de Servicios en Program.cs

La configuración de AspNetCoreRateLimit se realiza en el archivo Program.cs, donde se registran los servicios y se integra el midddleware en el pipeline de la aplicación. Es crucial configurar un almacenamiento para los contadores de solicitudes y definir cómo se cargan las reglas.

using AspNetCoreRateLimit;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.OpenApi.Models; // Para Swagger, si se usa

var appBuilder = WebApplication.CreateBuilder(args);

// --- Configuración de servicios de AspNetCoreRateLimit ---
// 1. Almacenamiento en caché en memoria para los contadores de solicitudes.
//    Para entornos de producción con múltiples instancias, se recomienda un almacenamiento distribuido.
appBuilder.Services.AddMemoryCache();

// 2. Cargar las opciones generales de limitación de tasa desde 'appsettings.json'.
appBuilder.Services.Configure<IpRateLimitOptions>(appBuilder.Configuration.GetSection("IpRateLimiting"));

// 3. Cargar las políticas de limitación de tasa específicas desde 'appsettings.json'.
appBuilder.Services.Configure<IpRateLimitPolicies>(appBuilder.Configuration.GetSection("IpRateLimitPolicies"));

// 4. Registrar la implementación de la limitación de tasa en memoria.
//    Como alternativa, se pueden usar implementaciones distribuidas como AddDistributedRateLimiting
//    con Redis o SQL Server para arquitecturas escalables.
appBuilder.Services.AddInMemoryRateLimiting();

// 5. Registrar servicios esenciales para el correcto funcionamiento de AspNetCoreRateLimit.
appBuilder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
appBuilder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();

// --- Otros servicios estándar de ASP.NET Core ---
appBuilder.Services.AddControllers();
appBuilder.Services.AddEndpointsApiExplorer(); // Habilitar API Explorer para Swagger/OpenAPI
appBuilder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "API de Prueba de Flujo", Version = "v1" });
    // Se pueden añadir más configuraciones de Swagger aquí, como comentarios XML o seguridad JWT.
});

var webApp = appBuilder.Build();

// --- Integración del middleware de AspNetCoreRateLimit ---
// Habilitar el middleware de limitación de tasa de IP.
// Debe registrarse ANTES de cualquier middleware que pueda generar respuestas de error
// o que se quiera proteger.
webApp.UseIpRateLimiting();

// --- Middleware estándar de ASP.NET Core ---
if (webApp.Environment.IsDevelopment())
{
    webApp.UseSwagger();
    webApp.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "API de Prueba de Flujo v1");
    });
}

// webApp.UseHttpsRedirection(); // Se puede habilitar si se desea
webApp.UseAuthorization(); // Habilitar si la aplicación utiliza autenticación/autorización

webApp.MapControllers(); // Mapea los controladores a las rutas de la API

webApp.Run();

En este código, se configura AddMemoryCache para almacenar los contadores de solicitudes, se vinculan las secciones IpRateLimiting y IpRateLimitPolicies del archivo de configuración a sus respectivas clases de opciones, y se registra AddInMemoryRateLimiting. Es fundamental registrar también IHttpContextAccessor y IRateLimitConfiguration como servicios singleton.

  1. Definición de Políticas de Limitación en appsettings.json

Las reglas de limitación de tasa se definen en el archivo appsettings.json. Esto permite una gestión flexible y sin necesidad de recompilar el código al cambiar las políticas.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",

  "IpRateLimiting": {
    // Si es 'true', las reglas se aplicarán a endpoints específicos (ej: GET:/api/recurso).
    // Si es 'false' (valor por defecto), las reglas generales se aplicarán globalmente.
    "EnableEndpointRateLimiting": false,
    // Si es 'true', las solicitudes bloqueadas se contarán en el límite de tasa.
    "StackBlockedRequests": false,
    // Encabezado HTTP para identificar la IP real del cliente (útil detrás de proxies/balanceadores de carga).
    "RealIpHeader": "X-Real-IP",
    // Encabezado HTTP para identificar a un cliente específico (ej: una clave de API).
    "ClientIdHeader": "X-ClientId",
    // Código de estado HTTP por defecto para respuestas de "demasiadas solicitudes".
    "HttpStatusCode": 429,
    // Respuesta personalizada cuando se excede la cuota.
    "QuotaExceededResponse": {
      "Content": "{ \"status\": 429, \"mensaje\": \"Demasiadas solicitudes. Por favor, inténtelo de nuevo más tarde.\" }",
      "ContentType": "application/json",
      "StatusCode": 429
    },
    // Lista de direcciones IP que estarán exentas de cualquier limitación.
    "IpWhitelist": [
      "127.0.0.1",
      "::1" // IPv6 localhost
    ],
    // Lista de endpoints que estarán exentos de limitación (ej: GET:/api/health).
    "EndpointWhitelist": [],
    // Lista de IDs de cliente (del ClientIdHeader) que estarán exentos de limitación.
    "ClientWhitelist": [],
    // Reglas generales que se aplican a todos los clientes por defecto.
    "GeneralRules": [
      {
        "Endpoint": "*", // Aplica a todos los endpoints.
        "Period": "5s",  // Período de 5 segundos.
        "Limit": 3       // Límite de 3 solicitudes en 5 segundos.
      },
      {
        "Endpoint": "*",
        "Period": "1m",  // Período de 1 minuto.
        "Limit": 20      // Límite de 20 solicitudes en 1 minuto.
      }
    ]
  },

  "IpRateLimitPolicies": {
    // Definición de reglas de limitación de tasa específicas por IP o rango de IPs.
    "IpRules": [
      {
        // Dirección IP específica o rango CIDR.
        "Ip": "84.247.85.224",
        "Rules": [
          {
            "Endpoint": "*",
            "Period": "1s",
            "Limit": 5 // 5 solicitudes por segundo para esta IP.
          },
          {
            "Endpoint": "*",
            "Period": "10m",
            "Limit": 100 // 100 solicitudes en 10 minutos para esta IP.
          }
        ]
      },
      {
        // Ejemplo de rango de IPs (subnet CIDR).
        "Ip": "192.168.3.0/24",
        "Rules": [
          {
            "Endpoint": "GET:/api/data", // Regla específica para un método y endpoint.
            "Period": "5s",
            "Limit": 1 // 1 solicitud en 5 segundos para este endpoint desde este rango.
          },
          {
            "Endpoint": "*",
            "Period": "1h",
            "Limit": 300 // 300 solicitudes por hora para cualquier endpoint desde este rango.
          }
        ]
      }
    ]
  }
}

En este ejemplo, se configuran dos secciones principales:

  • IpRateLimiting: Contiene configuraciones generales como la habiltiación de límites por endpoint, el encabezado para obtener la IP real (X-Real-IP es común cuando se usa un proxy inverso), y una respuesta personalizada cuando se excede la cuota. Las GeneralRules aplican a todos los clientes que no tienen una política específica.
  • IpRateLimitPolicies: Permite definir reglas de limitación específicas para direcciones IP o rangos de IP (notación CIDR). Cada regla puede especificar un Endpoint (* para todos), un Period (ej: "1s", "5m", "1h") y un Limit.

Es importante destacar que el StatusCode en QuotaExceededResponse debe coincidir con HttpStatusCode para una respuesta coherente, siendo el 429 Too Many Requests el estándar para esta situación.

Etiquetas: AspNetCoreRateLimit .NET6 RateLimiting ASP.NET Core Middleware

Publicado el 6-2 00:08