asp.net-core
Límite de velocidad
Buscar..
Observaciones
AspNetCoreRateLimit es una solución de código abierto de ASP.NET Core limitadora de la velocidad diseñada para controlar la tasa de solicitudes que los clientes pueden hacer a una aplicación de API o MVC web basada en la dirección IP o la identificación del cliente.
Limitación de tarifas basada en la IP del cliente
Con el middleware IpRateLimit puede establecer múltiples límites para diferentes escenarios, como permitir que un IP o rango de IP realice un número máximo de llamadas en un intervalo de tiempo como por segundo, 15 minutos, etc. Puede definir estos límites para abordar todas las solicitudes realizadas a un API o puede extender los límites a cada ruta URL o verbo y ruta HTTP.
Preparar
Instalación de NuGet :
Install-Package AspNetCoreRateLimit
Código de inicio.cs :
public void ConfigureServices(IServiceCollection services) { // needed to load configuration from appsettings.json services.AddOptions(); // needed to store rate limit counters and ip rules services.AddMemoryCache(); //load general configuration from appsettings.json services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting")); //load ip rules from appsettings.json services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies")); // inject counter and rules stores services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>(); services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>(); // Add framework services. services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseIpRateLimiting(); app.UseMvc(); }
Debe registrar el middleware antes que cualquier otro componente, excepto loggerFactory.
si carga el saldo de su aplicación, deberá usar IDistributedCache
con Redis o SQLServer para que todas las instancias de kestrel tengan el mismo almacén de límite de velocidad. En lugar de en las tiendas de memoria, debe inyectar las tiendas distribuidas de esta manera:
// inject counter and rules distributed cache stores services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
Configuración y reglas generales appsettings.json :
"IpRateLimiting": { "EnableEndpointRateLimiting": false, "StackBlockedRequests": false, "RealIpHeader": "X-Real-IP", "ClientIdHeader": "X-ClientId", "HttpStatusCode": 429, "IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ], "EndpointWhitelist": [ "get:/api/license", "*:/api/status" ], "ClientWhitelist": [ "dev-id-1", "dev-id-2" ], "GeneralRules": [ { "Endpoint": "*", "Period": "1s", "Limit": 2 }, { "Endpoint": "*", "Period": "15m", "Limit": 100 }, { "Endpoint": "*", "Period": "12h", "Limit": 1000 }, { "Endpoint": "*", "Period": "7d", "Limit": 10000 } ] }
Si EnableEndpointRateLimiting
se establece en false
, los límites se aplicarán globalmente y solo se aplicarán las reglas que tengan como punto final *
. Por ejemplo, si establece un límite de 5 llamadas por segundo, cualquier llamada HTTP a cualquier punto final contará hacia ese límite.
Si EnableEndpointRateLimiting
se establece en true
, los límites se aplicarán a cada punto final como en {HTTP_Verb}{PATH}
. Por ejemplo, si establece un límite de 5 llamadas por segundo para *:/api/values
un cliente puede llamar a GET /api/values
5 veces por segundo, pero también 5 veces PUT /api/values
.
Si StackBlockedRequests
está configurado como false
llamadas rechazadas no se agregan al contador del acelerador. Si un cliente hace 3 solicitudes por segundo y ha establecido un límite de una llamada por segundo, otros límites como los contadores por minuto o por día solo registrarán la primera llamada, la que no se bloqueó. Si desea que las solicitudes rechazadas cuenten hacia los otros límites, tendrá que establecer StackBlockedRequests
en true
.
RealIpHeader
se usa para extraer la IP del cliente cuando su servidor Kestrel está detrás de un proxy inverso, si su proxy usa un encabezado diferente, entonces X-Real-IP
usa esta opción para configurarlo.
ClientIdHeader
se utiliza para extraer el ID de cliente para la lista blanca, si un ID de cliente está presente en este encabezado y coincide con un valor especificado en ClientWhitelist, entonces no se aplican límites de tasa.
Reemplace las reglas generales para IPs específicas appsettings.json :
"IpRateLimitPolicies": { "IpRules": [ { "Ip": "84.247.85.224", "Rules": [ { "Endpoint": "*", "Period": "1s", "Limit": 10 }, { "Endpoint": "*", "Period": "15m", "Limit": 200 } ] }, { "Ip": "192.168.3.22/25", "Rules": [ { "Endpoint": "*", "Period": "1s", "Limit": 5 }, { "Endpoint": "*", "Period": "15m", "Limit": 150 }, { "Endpoint": "*", "Period": "12h", "Limit": 500 } ] } ] }
El campo IP admite valores de IP v4 y v6 y rangos como "192.168.0.0/24", "fe80 :: / 10" o "192.168.0.0-192.168.0.255".
Definir reglas de límite de velocidad.
Una regla se compone de un punto final, un período y un límite.
El formato del punto final es {HTTP_Verb}:{PATH}
, puede apuntar a cualquier verbo HTTP usando el símbolo de asterisco.
El formato del período es {INT}{PERIOD_TYPE}
, puede usar uno de los siguientes tipos de período: s, m, h, d
.
El formato límite es {LONG}
.
Ejemplos :
Límite de velocidad de todos los puntos finales a 2 llamadas por segundo:
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
Si, desde la misma IP, en el mismo segundo, hará 3 llamadas GET a api / valores, la última llamada se bloqueará. Pero si en el mismo segundo también llama PUT api / valores, la solicitud se procesará porque es un punto final diferente. Cuando se habilita la limitación de la velocidad del punto final, cada llamada está limitada en función de {HTTP_Verb}{PATH}
.
Limite las llamadas con cualquier verbo HTTP a /api/values
a 5 llamadas por 15 minutos:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
Límite de velocidad GET llamada a /api/values
a 5 llamadas por hora:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
Si, desde la misma IP, en una hora, hará 6 llamadas GET a api / valores, la última llamada se bloqueará. Pero si en la misma hora también llama a GET api / values / 1, la solicitud se realizará porque es un punto final diferente.
Comportamiento
Cuando un cliente realiza una llamada HTTP, IpRateLimitMiddleware hace lo siguiente:
- extrae la IP, el ID del cliente, el verbo HTTP y la URL del objeto de solicitud; si desea implementar su propia lógica de extracción, puede anular
IpRateLimitMiddleware.SetIdentity
- busca la IP, el ID del cliente y la URL en las listas blancas, si hay alguna coincidencia, no se realiza ninguna acción
- busca en las reglas de IP para una coincidencia, todas las reglas que aplican se agrupan por período, para cada período se usa la regla más restrictiva
- busca en las reglas generales una coincidencia, si una regla general que coincide tiene un período definido que no está presente en las reglas de IP, esta regla general también se usa
- para cada regla coincidente, el contador del límite de la tasa se incrementa, si el valor del contador es mayor que el límite de la regla, la solicitud se bloquea
Si la solicitud se bloquea, el cliente recibe una respuesta de texto como esta:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
Puede personalizar la respuesta cambiando estas opciones HttpStatusCode
y QuotaExceededMessage
. Si desea implementar su propia respuesta, puede anular IpRateLimitMiddleware.ReturnQuotaExceededResponse
. El valor del encabezado Retry-After
se expresa en segundos.
Si la solicitud no obtiene una tasa limitada, el período más largo definido en las reglas de coincidencia se usa para componer los encabezados de X-Rate-Limit, estos encabezados se inyectan en la respuesta:
X-Rate-Limit-Limit: the rate limit period (eg. 1m, 12h, 1d)
X-Rate-Limit-Remaining: number of request remaining
X-Rate-Limit-Reset: UTC date time when the limits resets
De forma predeterminada, las solicitudes bloqueadas se registran con Microsoft.Extensions.Logging.ILogger
. Si desea implementar su propio registro, puede anular IpRateLimitMiddleware.LogBlockedRequest
. El registrador predeterminado emite la siguiente información cuando una solicitud obtiene una tasa limitada:
info: AspNetCoreRateLimit.IpRateLimitMiddleware[0]
Request get:/api/values from IP 84.247.85.224 has been blocked, quota 2/1m exceeded by 3. Blocked by rule *:/api/value, TraceIdentifier 0HKTLISQQVV9D.
Actualizar los límites de la tasa en tiempo de ejecución
Al inicio de la aplicación, las reglas de límite de velocidad de IP definidas en appsettings.json
se cargan en la memoria caché por MemoryCacheClientPolicyStore
o DistributedCacheIpPolicyStore
dependiendo del tipo de proveedor de memoria caché que esté utilizando. Puede acceder al almacén de políticas de IP dentro de un controlador y modificar las reglas de IP de la siguiente manera:
public class IpRateLimitController : Controller { private readonly IpRateLimitOptions _options; private readonly IIpPolicyStore _ipPolicyStore; public IpRateLimitController(IOptions<IpRateLimitOptions> optionsAccessor, IIpPolicyStore ipPolicyStore) { _options = optionsAccessor.Value; _ipPolicyStore = ipPolicyStore; } [HttpGet] public IpRateLimitPolicies Get() { return _ipPolicyStore.Get(_options.IpPolicyPrefix); } [HttpPost] public void Post() { var pol = _ipPolicyStore.Get(_options.IpPolicyPrefix); pol.IpRules.Add(new IpRateLimitPolicy { Ip = "8.8.4.4", Rules = new List<RateLimitRule>(new RateLimitRule[] { new RateLimitRule { Endpoint = "*:/api/testupdate", Limit = 100, Period = "1d" } }) }); _ipPolicyStore.Set(_options.IpPolicyPrefix, pol); } }
De esta manera, puede almacenar los límites de velocidad de IP en una base de datos y empujarlos en el caché después de que se inicie cada aplicación.
Limitación de tarifas basada en la identificación del cliente
Con el middleware ClientRateLimit puede establecer múltiples límites para diferentes escenarios, como permitir que un Cliente realice un número máximo de llamadas en un intervalo de tiempo como por segundo, 15 minutos, etc. Puede definir estos límites para abordar todas las solicitudes realizadas a una API o puede abarcar los límites de cada ruta URL o verbo y ruta HTTP.
Preparar
Instalación de NuGet :
Install-Package AspNetCoreRateLimit
Código de inicio.cs :
public void ConfigureServices(IServiceCollection services) { // needed to load configuration from appsettings.json services.AddOptions(); // needed to store rate limit counters and ip rules services.AddMemoryCache(); //load general configuration from appsettings.json services.Configure<ClientRateLimitOptions>(Configuration.GetSection("ClientRateLimiting")); //load client rules from appsettings.json services.Configure<ClientRateLimitPolicies>(Configuration.GetSection("ClientRateLimitPolicies")); // inject counter and rules stores services.AddSingleton<IClientPolicyStore, MemoryCacheClientPolicyStore>(); services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>(); // Add framework services. services.AddMvc(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseClientRateLimiting(); app.UseMvc(); }
Debe registrar el middleware antes que cualquier otro componente, excepto loggerFactory.
si carga el saldo de su aplicación, deberá usar IDistributedCache
con Redis o SQLServer para que todas las instancias de kestrel tengan el mismo almacén de límite de velocidad. En lugar de en las tiendas de memoria, debe inyectar las tiendas distribuidas de esta manera:
// inject counter and rules distributed cache stores services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
Configuración y reglas generales appsettings.json :
"ClientRateLimiting": { "EnableEndpointRateLimiting": false, "StackBlockedRequests": false, "ClientIdHeader": "X-ClientId", "HttpStatusCode": 429, "EndpointWhitelist": [ "get:/api/license", "*:/api/status" ], "ClientWhitelist": [ "dev-id-1", "dev-id-2" ], "GeneralRules": [ { "Endpoint": "*", "Period": "1s", "Limit": 2 }, { "Endpoint": "*", "Period": "15m", "Limit": 100 }, { "Endpoint": "*", "Period": "12h", "Limit": 1000 }, { "Endpoint": "*", "Period": "7d", "Limit": 10000 } ] }
Si EnableEndpointRateLimiting
se establece en false
, los límites se aplicarán globalmente y solo se aplicarán las reglas que tengan como punto final *
. Por ejemplo, si establece un límite de 5 llamadas por segundo, cualquier llamada HTTP a cualquier punto final contará hacia ese límite.
Si EnableEndpointRateLimiting
se establece en true
, los límites se aplicarán a cada punto final como en {HTTP_Verb}{PATH}
. Por ejemplo, si establece un límite de 5 llamadas por segundo para *:/api/values
un cliente puede llamar a GET /api/values
5 veces por segundo, pero también 5 veces PUT /api/values
.
Si StackBlockedRequests
está configurado como false
llamadas rechazadas no se agregan al contador del acelerador. Si un cliente hace 3 solicitudes por segundo y ha establecido un límite de una llamada por segundo, otros límites como los contadores por minuto o por día solo registrarán la primera llamada, la que no se bloqueó. Si desea que las solicitudes rechazadas cuenten hacia los otros límites, tendrá que establecer StackBlockedRequests
en true
.
El ClientIdHeader
se usa para extraer el ID de cliente, si un ID de cliente está presente en este encabezado y coincide con un valor especificado en ClientWhitelist, entonces no se aplican límites de tasa.
Reemplace las reglas generales para clientes específicos appsettings.json :
"ClientRateLimitPolicies": { "ClientRules": [ { "ClientId": "client-id-1", "Rules": [ { "Endpoint": "*", "Period": "1s", "Limit": 10 }, { "Endpoint": "*", "Period": "15m", "Limit": 200 } ] }, { "Client": "client-id-2", "Rules": [ { "Endpoint": "*", "Period": "1s", "Limit": 5 }, { "Endpoint": "*", "Period": "15m", "Limit": 150 }, { "Endpoint": "*", "Period": "12h", "Limit": 500 } ] } ] }
Definir reglas de límite de velocidad.
Una regla se compone de un punto final, un período y un límite.
El formato del punto final es {HTTP_Verb}:{PATH}
, puede apuntar a cualquier verbo HTTP usando el símbolo de asterisco.
El formato del período es {INT}{PERIOD_TYPE}
, puede usar uno de los siguientes tipos de período: s, m, h, d
.
El formato límite es {LONG}
.
Ejemplos :
Límite de velocidad de todos los puntos finales a 2 llamadas por segundo:
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
Si en el mismo segundo, un cliente realiza 3 llamadas GET a api / valores, la última llamada se bloqueará. Pero si en el mismo segundo también llama PUT api / valores, la solicitud se realizará porque es un punto final diferente. Cuando se habilita la limitación de la velocidad del punto final, cada llamada está limitada en función de {HTTP_Verb}{PATH}
.
Limite las llamadas con cualquier verbo HTTP a /api/values
a 5 llamadas por 15 minutos:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
Límite de velocidad GET llamada a /api/values
a 5 llamadas por hora:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
Si en una hora, un cliente realiza 6 llamadas GET a api / valores, la última llamada se bloqueará. Pero si en la misma hora también llama a GET api / values / 1, la solicitud se realizará porque es un punto final diferente.
Comportamiento
Cuando un cliente realiza una llamada HTTP, ClientRateLimitMiddleware hace lo siguiente:
- extrae el ID del cliente, el verbo HTTP y la URL del objeto de solicitud. Si desea implementar su propia lógica de extracción, puede anular el
ClientRateLimitMiddleware.SetIdentity
- busca el ID del cliente y la URL en las listas blancas, si hay alguna coincidencia, no se realiza ninguna acción
- busca en las reglas del Cliente una coincidencia, todas las reglas que aplican se agrupan por período, para cada período se usa la regla más restrictiva
- busca en las reglas generales una coincidencia, si una regla general que coincida tiene un período definido que no está presente en las reglas del cliente, esta regla general también se usa
- para cada regla coincidente, el contador del límite de la tasa se incrementa, si el valor del contador es mayor que el límite de la regla, la solicitud se bloquea
Si la solicitud se bloquea, el cliente recibe una respuesta de texto como esta:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
Puede personalizar la respuesta cambiando estas opciones HttpStatusCode
y QuotaExceededMessage
. Si desea implementar su propia respuesta, puede anular el ClientRateLimitMiddleware.ReturnQuotaExceededResponse
. El valor del encabezado Retry-After
se expresa en segundos.
Si la solicitud no obtiene una tasa limitada, el período más largo definido en las reglas de coincidencia se usa para componer los encabezados de X-Rate-Limit, estos encabezados se inyectan en la respuesta:
X-Rate-Limit-Limit: the rate limit period (eg. 1m, 12h, 1d)
X-Rate-Limit-Remaining: number of request remaining
X-Rate-Limit-Reset: UTC date time when the limits resets
De forma predeterminada, las solicitudes bloqueadas se registran con Microsoft.Extensions.Logging.ILogger
. Si desea implementar su propio registro, puede anular el ClientRateLimitMiddleware.LogBlockedRequest
. El registrador predeterminado emite la siguiente información cuando una solicitud obtiene una tasa limitada:
info: AspNetCoreRateLimit.ClientRateLimitMiddleware[0]
Request get:/api/values from ClientId client-id-1 has been blocked, quota 2/1m exceeded by 3. Blocked by rule *:/api/value, TraceIdentifier 0HKTLISQQVV9D.
Actualizar los límites de la tasa en tiempo de ejecución
Al inicio de la aplicación, las reglas de límite de frecuencia del cliente definidas en appsettings.json
se cargan en la memoria caché por MemoryCacheClientPolicyStore
o DistributedCacheClientPolicyStore
dependiendo del tipo de proveedor de memoria caché que esté utilizando. Puede acceder al almacén de políticas del cliente dentro de un controlador y modificar las reglas así:
public class ClientRateLimitController : Controller { private readonly ClientRateLimitOptions _options; private readonly IClientPolicyStore _clientPolicyStore; public ClientRateLimitController(IOptions<ClientRateLimitOptions> optionsAccessor, IClientPolicyStore clientPolicyStore) { _options = optionsAccessor.Value; _clientPolicyStore = clientPolicyStore; } [HttpGet] public ClientRateLimitPolicy Get() { return _clientPolicyStore.Get($"{_options.ClientPolicyPrefix}_cl-key-1"); } [HttpPost] public void Post() { var id = $"{_options.ClientPolicyPrefix}_cl-key-1"; var clPolicy = _clientPolicyStore.Get(id); clPolicy.Rules.Add(new RateLimitRule { Endpoint = "*/api/testpolicyupdate", Period = "1h", Limit = 100 }); _clientPolicyStore.Set(id, clPolicy); } }
De esta manera, puede almacenar los límites de velocidad de los clientes en una base de datos y empujarlos en el caché después de que se inicie cada aplicación.