asp.net-core
Limitation de débit
Recherche…
Remarques
AspNetCoreRateLimit est une solution de limitation de débit ASP.NET Core open source conçue pour contrôler le taux de requêtes que les clients peuvent adresser à une API Web ou à une application MVC en fonction de l'adresse IP ou de l'ID client.
Limitation de débit basée sur l'adresse IP du client
Avec le middleware IpRateLimit, vous pouvez définir plusieurs limites pour différents scénarios, comme autoriser une plage IP ou IP à effectuer un nombre maximum d'appels dans un intervalle de temps de 15 minutes, etc. Vous pouvez définir ces limites pour adresser toutes les requêtes API ou vous pouvez définir les limites de chaque chemin d'URL ou verbe et chemin HTTP.
Installer
NuGet installer :
Install-Package AspNetCoreRateLimit
Code Startup.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(); }
Vous devez enregistrer le middleware avant tout autre composant, à l'exception de loggerFactory.
Si vous chargez votre application, vous devez utiliser IDistributedCache
avec Redis ou SQLServer pour que toutes les instances de IDistributedCache
aient le même magasin de limites de débit. Au lieu des magasins en mémoire, vous devez injecter les magasins distribués comme ceci:
// inject counter and rules distributed cache stores services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
Configuration et règles générales 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
est défini sur false
les limites s'appliquent globalement et seules les règles ayant pour point de terminaison *
seront appliquées. Par exemple, si vous définissez une limite de 5 appels par seconde, tout appel HTTP à un noeud final sera comptabilisé dans cette limite.
Si EnableEndpointRateLimiting
est défini sur true
les limites s'appliquent à chaque noeud final, comme dans {HTTP_Verb}{PATH}
. Par exemple, si vous définissez une limite de 5 appels par seconde pour *:/api/values
un client peut appeler GET /api/values
5 fois par seconde mais également 5 fois PUT /api/values
.
Si StackBlockedRequests
est défini sur false
appels rejetés ne sont pas ajoutés au compteur d'accélération. Si un client effectue 3 requêtes par seconde et que vous définissez une limite d'un appel par seconde, les autres limites, telles que les compteurs par minute ou par jour, enregistrent uniquement le premier appel, celui qui n'a pas été bloqué. Si vous souhaitez que les demandes rejetées soient comptabilisées dans les autres limites, vous devez définir StackBlockedRequests
sur true
.
RealIpHeader
est utilisé pour extraire l'adresse IP du client lorsque votre serveur Kestrel se trouve derrière un proxy inverse. Si votre proxy utilise un en-tête différent, X-Real-IP
utilise cette option pour le configurer.
ClientIdHeader
est utilisé pour extraire l'ID client pour la liste blanche, si un identifiant client est présent dans cet en-tête et correspond à une valeur spécifiée dans ClientWhitelist, aucune limite de taux n'est appliquée.
Remplacez les règles générales pour des adresses IP spécifiques 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 } ] } ] }
Le champ IP prend en charge les valeurs IP v4 et v6 et se situe entre "192.168.0.0/24", "fe80 :: / 10" ou "192.168.0.0-192.168.0.255".
Définition des règles de limite de taux
Une règle est composée d'un noeud final, d'une période et d'une limite.
Le format du point de terminaison est {HTTP_Verb}:{PATH}
, vous pouvez cibler n'importe quel verbe HTTP en utilisant le symbole astérisque.
Le format de période est {INT}{PERIOD_TYPE}
, vous pouvez utiliser l'un des types de période suivants: s, m, h, d
.
Le format de limite est {LONG}
.
Exemples :
Le taux limite tous les points de terminaison à 2 appels par seconde:
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
Si, à partir de la même adresse IP, dans la même seconde, vous effectuerez 3 appels GET vers api / valeurs, le dernier appel sera bloqué. Mais si, dans la même seconde, vous appelez PIT api / values, la requête sera transmise parce que le point de terminaison est différent. Lorsque la limitation de la fréquence d'extrémité est activée, chaque appel est limité par le taux basé sur {HTTP_Verb}{PATH}
.
Le taux limite les appels avec n'importe quel verbe HTTP à /api/values
à 5 appels par 15 minutes:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
Taux limite GET appel à /api/values
à 5 appels par heure:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
Si, à partir de la même adresse IP, vous effectuez 6 appels GET vers api / valeurs, le dernier appel sera bloqué. Mais si, dans la même heure, vous appelez GET api / values / 1, la requête est transmise parce que le point de terminaison est différent.
Comportement
Lorsqu'un client effectue un appel HTTP, IpRateLimitMiddleware effectue les opérations suivantes:
- extrait l'adresse IP, l'identifiant du client, le verbe HTTP et l'URL de l'objet de requête, si vous souhaitez implémenter votre propre logique d'extraction, vous pouvez remplacer l'
IpRateLimitMiddleware.SetIdentity
- recherche l'adresse IP, l'identifiant du client et l'URL dans les listes blanches, s'il y a des correspondances, aucune action n'est prise
- recherche dans les règles IP pour une correspondance, toutes les règles qui s'appliquent sont regroupées par période, pour chaque période la règle la plus restrictive est utilisée
- recherche dans les règles générales une correspondance, si une règle générale qui correspond à une période définie n'est pas présente dans les règles IP, cette règle générale est également utilisée
- pour chaque règle de correspondance, le compteur de limite de débit est incrémenté, si la valeur du compteur est supérieure à la limite de la règle, la requête est bloquée
Si la requête est bloquée, le client reçoit une réponse textuelle comme ceci:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
Vous pouvez personnaliser la réponse en modifiant ces options HttpStatusCode
et QuotaExceededMessage
. Si vous souhaitez implémenter votre propre réponse, vous pouvez remplacer IpRateLimitMiddleware.ReturnQuotaExceededResponse
. La valeur de l'en Retry-After
tête Retry-After
est exprimée en secondes.
Si la demande ne reçoit pas de taux limité, la plus longue période définie dans les règles de correspondance est utilisée pour composer les en-têtes X-Rate-Limit, ces en-têtes sont injectés dans la réponse:
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
Par défaut, les requêtes bloquées sont consignées à l'aide de Microsoft.Extensions.Logging.ILogger
. Si vous souhaitez implémenter votre propre journalisation, vous pouvez remplacer IpRateLimitMiddleware.LogBlockedRequest
. Le consignateur par défaut émet les informations suivantes lorsqu'une demande reçoit un taux limité:
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.
Mettre à jour les limites de taux lors de l'exécution
Au démarrage de l'application, les règles de limite de débit IP définies dans appsettings.json
sont chargées dans le cache par MemoryCacheClientPolicyStore
ou DistributedCacheIpPolicyStore
selon le type de fournisseur de cache utilisé. Vous pouvez accéder au magasin de règles Ip dans un contrôleur et modifier les règles IP comme suit:
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 cette façon, vous pouvez stocker les limites de taux IP dans une base de données et les mettre en cache après le démarrage de chaque application.
Limitation du taux basé sur l'ID du client
Avec le middleware ClientRateLimit, vous pouvez définir plusieurs limites pour différents scénarios, comme autoriser un client à effectuer un nombre maximal d'appels par intervalle de temps, par seconde, 15 minutes, etc. Vous pouvez définir ces limites pour adresser toutes les requêtes adressées à une API peut couvrir les limites de chaque chemin d'URL ou de verbe et chemin HTTP.
Installer
NuGet installer :
Install-Package AspNetCoreRateLimit
Code Startup.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(); }
Vous devez enregistrer le middleware avant tout autre composant, à l'exception de loggerFactory.
Si vous chargez votre application, vous devez utiliser IDistributedCache
avec Redis ou SQLServer pour que toutes les instances de IDistributedCache
aient le même magasin de limites de débit. Au lieu des magasins en mémoire, vous devez injecter les magasins distribués comme ceci:
// inject counter and rules distributed cache stores services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
Configuration et règles générales 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
est défini sur false
les limites s'appliquent globalement et seules les règles ayant pour point de terminaison *
seront appliquées. Par exemple, si vous définissez une limite de 5 appels par seconde, tout appel HTTP à un noeud final sera comptabilisé dans cette limite.
Si EnableEndpointRateLimiting
est défini sur true
les limites s'appliquent à chaque noeud final, comme dans {HTTP_Verb}{PATH}
. Par exemple, si vous définissez une limite de 5 appels par seconde pour *:/api/values
un client peut appeler GET /api/values
5 fois par seconde mais également 5 fois PUT /api/values
.
Si StackBlockedRequests
est défini sur false
appels rejetés ne sont pas ajoutés au compteur d'accélération. Si un client effectue 3 requêtes par seconde et que vous définissez une limite d'un appel par seconde, les autres limites, telles que les compteurs par minute ou par jour, enregistrent uniquement le premier appel, celui qui n'a pas été bloqué. Si vous souhaitez que les demandes rejetées soient comptabilisées dans les autres limites, vous devez définir StackBlockedRequests
sur true
.
ClientIdHeader
est utilisé pour extraire l'ID du client, si un identifiant de client est présent dans cet en-tête et correspond à une valeur spécifiée dans ClientWhitelist, aucune limite de taux n'est appliquée.
Remplacez les règles générales pour des clients spécifiques 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 } ] } ] }
Définition des règles de limite de taux
Une règle est composée d'un noeud final, d'une période et d'une limite.
Le format du point de terminaison est {HTTP_Verb}:{PATH}
, vous pouvez cibler n'importe quel verbe HTTP en utilisant le symbole astérisque.
Le format de période est {INT}{PERIOD_TYPE}
, vous pouvez utiliser l'un des types de période suivants: s, m, h, d
.
Le format de limite est {LONG}
.
Exemples :
Le taux limite tous les points de terminaison à 2 appels par seconde:
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
Si, dans la même seconde, un client effectue 3 appels GET vers api / valeurs, le dernier appel sera bloqué. Mais si, dans la même seconde, il appelle PIT api / values, la requête passera par un point final différent. Lorsque la limitation de la fréquence d'extrémité est activée, chaque appel est limité par le taux basé sur {HTTP_Verb}{PATH}
.
Le taux limite les appels avec n'importe quel verbe HTTP à /api/values
à 5 appels par 15 minutes:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
Taux limite GET appel à /api/values
à 5 appels par heure:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
Si dans une heure, un client effectue 6 appels GET vers api / valeurs, le dernier appel sera bloqué. Mais si dans la même heure il appelle GET api / values / 1 aussi, la requête passera parce que c'est un point final différent.
Comportement
Lorsqu'un client effectue un appel HTTP, ClientRateLimitMiddleware effectue les opérations suivantes:
- extrait l'ID du client, le verbe HTTP et l'URL de l'objet de requête, si vous souhaitez implémenter votre propre logique d'extraction, vous pouvez remplacer
ClientRateLimitMiddleware.SetIdentity
- recherche l'identifiant et l'URL du client dans les listes blanches, s'il y a des correspondances, aucune action n'est prise
- recherche dans le client les règles d'une correspondance, toutes les règles qui s'appliquent sont regroupées par période, pour chaque période la règle la plus restrictive est utilisée
- recherche dans les règles générales une correspondance, si une règle générale qui correspond à une période définie est absente des règles du client, cette règle générale est également utilisée
- pour chaque règle de correspondance, le compteur de limite de débit est incrémenté, si la valeur du compteur est supérieure à la limite de la règle, la requête est bloquée
Si la requête est bloquée, le client reçoit une réponse textuelle comme ceci:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
Vous pouvez personnaliser la réponse en modifiant ces options HttpStatusCode
et QuotaExceededMessage
. Si vous souhaitez implémenter votre propre réponse, vous pouvez remplacer ClientRateLimitMiddleware.ReturnQuotaExceededResponse
. La valeur de l'en Retry-After
tête Retry-After
est exprimée en secondes.
Si la demande ne reçoit pas de taux limité, la plus longue période définie dans les règles de correspondance est utilisée pour composer les en-têtes X-Rate-Limit, ces en-têtes sont injectés dans la réponse:
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
Par défaut, les requêtes bloquées sont consignées à l'aide de Microsoft.Extensions.Logging.ILogger
. Si vous souhaitez implémenter votre propre journalisation, vous pouvez remplacer ClientRateLimitMiddleware.LogBlockedRequest
. Le consignateur par défaut émet les informations suivantes lorsqu'une demande reçoit un taux limité:
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.
Mettre à jour les limites de taux lors de l'exécution
Au démarrage de l'application, les règles de limite de débit du client définies dans appsettings.json
sont chargées dans le cache par MemoryCacheClientPolicyStore
ou DistributedCacheClientPolicyStore
selon le type de fournisseur de cache utilisé. Vous pouvez accéder au magasin de règles client dans un contrôleur et modifier les règles comme suit:
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 cette façon, vous pouvez stocker les limites de taux client dans une base de données et les mettre en cache après le démarrage de chaque application.