asp.net-core
Limitazione di velocità
Ricerca…
Osservazioni
AspNetCoreRateLimit è una soluzione di limitazione della velocità di base di ASP.NET open source progettata per controllare la velocità delle richieste che i client possono eseguire su un'API Web o un'app MVC in base all'indirizzo IP o all'ID client.
Limitazione della velocità in base all'IP del cliente
Con il middleware IpRateLimit è possibile impostare più limiti per diversi scenari come consentire a un intervallo IP o IP di effettuare un numero massimo di chiamate in un intervallo di tempo come al secondo, 15 minuti, ecc. È possibile definire questi limiti per indirizzare tutte le richieste fatte ad un API o puoi limitare i limiti a ciascun percorso URL o verbo e percorso HTTP.
Impostare
Installa NuGet :
Install-Package AspNetCoreRateLimit
Codice 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(); }
È necessario registrare il middleware prima di qualsiasi altro componente tranne loggerFactory.
se si carica il bilancio dell'app, sarà necessario utilizzare IDistributedCache
con Redis o SQLServer in modo che tutte le istanze del gheppio abbiano lo stesso limite di frequenza. Invece dei negozi in memoria dovresti iniettare i negozi distribuiti in questo modo:
// inject counter and rules distributed cache stores services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
Configurazione e regole generali 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 } ] }
Se EnableEndpointRateLimiting
è impostato su false
i limiti verranno applicati globalmente e verranno applicate solo le regole che hanno come endpoint *
. Ad esempio, se imposti un limite di 5 chiamate al secondo, qualsiasi chiamata HTTP verso qualsiasi endpoint verrà conteggiata a tale limite.
Se EnableEndpointRateLimiting
è impostato su true
i limiti si applicano a ciascun endpoint come in {HTTP_Verb}{PATH}
. Ad esempio, se si imposta un limite di 5 chiamate al secondo per *:/api/values
un client può chiamare GET /api/values
5 volte al secondo, ma anche 5 volte PUT /api/values
.
Se StackBlockedRequests
è impostato su false
chiamate rifiutate non vengono aggiunte al contatore del throttle. Se un cliente effettua 3 richieste al secondo e hai impostato un limite di una chiamata al secondo, altri limiti come i contatori al minuto o al giorno registreranno solo la prima chiamata, quella che non è stata bloccata. Se vuoi che le richieste respinte StackBlockedRequests
conteggiate rispetto agli altri limiti, dovrai impostare StackBlockedRequests
su true
.
RealIpHeader
viene utilizzato per estrarre l'IP del client quando il server Kestrel si trova dietro un proxy inverso, se il proxy utilizza un'intestazione diversa, quindi X-Real-IP
utilizza questa opzione per configurarlo.
ClientIdHeader
viene utilizzato per estrarre l'ID client per l'elenco bianco, se un ID client è presente in questa intestazione e corrisponde a un valore specificato in ClientWhitelist, quindi non vengono applicati limiti di velocità.
Sovrascrivi le regole generali per specifici IP 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 } ] } ] }
Il campo IP supporta i valori IP v4 e v6 e intervalli come "192.168.0.0/24", "fe80 :: / 10" o "192.168.0.0-192.168.0.255".
Definizione delle regole del limite di velocità
Una regola è composta da un endpoint, un periodo e un limite.
Il formato {HTTP_Verb}:{PATH}
è {HTTP_Verb}:{PATH}
, puoi indirizzare qualunque verbo HTTP usando il simbolo asterix.
Il formato del periodo è {INT}{PERIOD_TYPE}
, puoi utilizzare uno dei seguenti tipi di periodo: s, m, h, d
.
Il formato limite è {LONG}
.
Esempi :
Limita il numero di tutti i punti finali a 2 chiamate al secondo:
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
Se, dallo stesso IP, nello stesso secondo, effettuerai 3 chiamate GET su API / valori, l'ultima chiamata verrà bloccata. Ma se nello stesso secondo si chiama PUT api / valori, la richiesta passerà perché è un diverso endpoint. Quando la limitazione della velocità dell'endpoint è abilitata, ogni chiamata viene limitata in base a {HTTP_Verb}{PATH}
.
Limite di velocità chiamate con qualsiasi Verbo HTTP a /api/values
a 5 chiamate per 15 minuti:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
Limite di velocità GET chiamata a /api/values
a 5 chiamate all'ora:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
Se, dallo stesso IP, in un'ora, effettuerai 6 GET chiamate su API / valori, l'ultima chiamata verrà bloccata. Ma se nella stessa ora chiami GET api / values / 1, la richiesta verrà eseguita perché si tratta di un diverso endpoint.
Comportamento
Quando un client effettua una chiamata HTTP, IpRateLimitMiddleware procede come segue:
- estrae IP, ID client, verbo HTTP e URL dall'oggetto richiesta, se vuoi implementare la tua logica di estrazione puoi sovrascrivere
IpRateLimitMiddleware.SetIdentity
- cerca l'IP, l'ID del cliente e l'URL nelle liste bianche, in caso di corrispondenza, non viene eseguita alcuna azione
- cerca nelle regole IP per una corrispondenza, tutte le regole che si applicano sono raggruppate per periodo, per ogni periodo viene utilizzata la regola più restrittiva
- cerca nelle regole generali per una corrispondenza, se una regola generale che corrisponde ha un periodo definito che non è presente nelle regole IP, viene utilizzata anche questa regola generale
- per ogni regola corrispondente il contatore del limite di velocità viene incrementato, se il valore del contatore è maggiore del limite della regola, allora la richiesta viene bloccata
Se la richiesta viene bloccata, il client riceve una risposta testuale come questa:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
È possibile personalizzare la risposta modificando queste opzioni HttpStatusCode
e QuotaExceededMessage
, se si desidera implementare la propria risposta è possibile sovrascrivere IpRateLimitMiddleware.ReturnQuotaExceededResponse
. Il valore dell'intestazione Retry-After
è espresso in secondi.
Se la richiesta non ottiene un tasso limitato, il periodo più lungo definito nelle regole di corrispondenza viene utilizzato per comporre le intestazioni X-Rate-Limit, queste intestazioni vengono iniettate nella risposta:
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
Per impostazione predefinita, la richiesta bloccata viene registrata utilizzando Microsoft.Extensions.Logging.ILogger
, se si desidera implementare la propria registrazione, è possibile sovrascrivere IpRateLimitMiddleware.LogBlockedRequest
. Il logger predefinito emette le seguenti informazioni quando una richiesta ottiene un limite tariffario:
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.
Aggiorna i limiti di velocità in fase di esecuzione
All'avvio dell'applicazione, le regole del limite di velocità IP definite in appsettings.json
vengono caricate nella cache da MemoryCacheClientPolicyStore
o DistributedCacheIpPolicyStore
seconda del tipo di provider di cache che si sta utilizzando. È possibile accedere all'archivio dei criteri IP all'interno di un controller e modificare le regole IP in questo modo:
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); } }
In questo modo è possibile memorizzare i limiti di velocità IP in un database e inserirli nella cache dopo l'avvio di ogni app.
Limitazione della velocità in base all'ID cliente
Con il middleware ClientRateLimit puoi impostare più limiti per diversi scenari come consentire al Cliente di effettuare un numero massimo di chiamate in un intervallo di tempo come al secondo, 15 minuti, ecc. Puoi definire questi limiti per soddisfare tutte le richieste fatte a un'API o tu può raggiungere i limiti di ogni percorso URL o verbo e percorso HTTP.
Impostare
Installa NuGet :
Install-Package AspNetCoreRateLimit
Codice 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(); }
È necessario registrare il middleware prima di qualsiasi altro componente tranne loggerFactory.
se si carica il bilancio dell'app, sarà necessario utilizzare IDistributedCache
con Redis o SQLServer in modo che tutte le istanze del gheppio abbiano lo stesso limite di frequenza. Invece dei negozi in memoria dovresti iniettare i negozi distribuiti in questo modo:
// inject counter and rules distributed cache stores services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
Configurazione e regole generali 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 } ] }
Se EnableEndpointRateLimiting
è impostato su false
i limiti verranno applicati globalmente e verranno applicate solo le regole che hanno come endpoint *
. Ad esempio, se imposti un limite di 5 chiamate al secondo, qualsiasi chiamata HTTP verso qualsiasi endpoint verrà conteggiata a tale limite.
Se EnableEndpointRateLimiting
è impostato su true
i limiti si applicano a ciascun endpoint come in {HTTP_Verb}{PATH}
. Ad esempio, se si imposta un limite di 5 chiamate al secondo per *:/api/values
un client può chiamare GET /api/values
5 volte al secondo, ma anche 5 volte PUT /api/values
.
Se StackBlockedRequests
è impostato su false
chiamate rifiutate non vengono aggiunte al contatore del throttle. Se un cliente effettua 3 richieste al secondo e hai impostato un limite di una chiamata al secondo, altri limiti come i contatori al minuto o al giorno registreranno solo la prima chiamata, quella che non è stata bloccata. Se vuoi che le richieste respinte StackBlockedRequests
conteggiate rispetto agli altri limiti, dovrai impostare StackBlockedRequests
su true
.
ClientIdHeader
viene utilizzato per estrarre l'id del client, se un ID client è presente in questa intestazione e corrisponde a un valore specificato in ClientWhitelist, quindi non vengono applicati limiti di velocità.
Sostituisci le regole generali per client specifici 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 } ] } ] }
Definizione delle regole del limite di velocità
Una regola è composta da un endpoint, un periodo e un limite.
Il formato {HTTP_Verb}:{PATH}
è {HTTP_Verb}:{PATH}
, puoi indirizzare qualunque verbo HTTP usando il simbolo asterix.
Il formato del periodo è {INT}{PERIOD_TYPE}
, puoi utilizzare uno dei seguenti tipi di periodo: s, m, h, d
.
Il formato limite è {LONG}
.
Esempi :
Limita il numero di tutti i punti finali a 2 chiamate al secondo:
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
Se nello stesso secondo, un client effettua 3 chiamate GET su API / valori, l'ultima chiamata verrà bloccata. Ma se nello stesso secondo chiama anche PIP api / valori, la richiesta passerà perché è un diverso endpoint. Quando la limitazione della velocità dell'endpoint è abilitata, ogni chiamata viene limitata in base a {HTTP_Verb}{PATH}
.
Limite di velocità chiamate con qualsiasi Verbo HTTP a /api/values
a 5 chiamate per 15 minuti:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
Limite di velocità GET chiamata a /api/values
a 5 chiamate all'ora:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
Se in un'ora un client effettua 6 chiamate GET su API / valori, l'ultima chiamata verrà bloccata. Ma se nella stessa ora chiama GET api / values / 1, la richiesta passerà perché è un diverso endpoint.
Comportamento
Quando un client effettua una chiamata HTTP, ClientRateLimitMiddleware effettua quanto segue:
- estrae l'ID del client, il verbo HTTP e l'URL dall'oggetto della richiesta, se vuoi implementare la tua logica di estrazione puoi sovrascrivere il
ClientRateLimitMiddleware.SetIdentity
- cerca l'id e l'URL del cliente negli elenchi bianchi, in caso di corrispondenza, non viene eseguita alcuna azione
- cerca nelle regole del cliente una corrispondenza, tutte le regole che si applicano sono raggruppate per periodo, per ogni periodo viene utilizzata la regola più restrittiva
- cerca nelle regole generali per una corrispondenza, se una regola generale che corrisponde ha un periodo definito che non è presente nelle regole del client, allora viene utilizzata anche questa regola generale
- per ogni regola corrispondente il contatore del limite di velocità viene incrementato, se il valore del contatore è maggiore del limite della regola, allora la richiesta viene bloccata
Se la richiesta viene bloccata, il client riceve una risposta testuale come questa:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
È possibile personalizzare la risposta modificando queste opzioni HttpStatusCode
e QuotaExceededMessage
, se si desidera implementare la propria risposta è possibile sovrascrivere ClientRateLimitMiddleware.ReturnQuotaExceededResponse
. Il valore dell'intestazione Retry-After
è espresso in secondi.
Se la richiesta non ottiene un tasso limitato, il periodo più lungo definito nelle regole di corrispondenza viene utilizzato per comporre le intestazioni X-Rate-Limit, queste intestazioni vengono iniettate nella risposta:
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
Per impostazione predefinita, la richiesta bloccata viene registrata utilizzando Microsoft.Extensions.Logging.ILogger
, se si desidera implementare la propria registrazione, è possibile ignorare ClientRateLimitMiddleware.LogBlockedRequest
. Il logger predefinito emette le seguenti informazioni quando una richiesta ottiene un limite tariffario:
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.
Aggiorna i limiti di velocità in fase di esecuzione
All'avvio dell'applicazione, le regole del limite di frequenza client definite in appsettings.json
vengono caricate nella cache da MemoryCacheClientPolicyStore
o DistributedCacheClientPolicyStore
seconda del tipo di provider di cache che si sta utilizzando. È possibile accedere all'archivio delle politiche client all'interno di un controller e modificare le regole in questo modo:
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); } }
In questo modo è possibile memorizzare i limiti di velocità del client in un database e inserirli nella cache dopo l'avvio di ogni app.