asp.net-core
Ograniczanie prędkości
Szukaj…
Uwagi
AspNetCoreRateLimit to rozwiązanie ASP.NET Core o otwartym kodzie źródłowym, zaprojektowane w celu kontroli liczby żądań klientów do interfejsu API sieci Web lub aplikacji MVC na podstawie adresu IP lub identyfikatora klienta.
Ograniczanie prędkości na podstawie adresu IP klienta
Dzięki oprogramowaniu pośredniczącemu IpRateLimit możesz ustawić wiele limitów dla różnych scenariuszy, np. Zezwalając, aby IP lub zakres adresów IP wykonywał maksymalną liczbę połączeń w odstępach czasowych, takich jak sekunda, 15 minut itp. Możesz zdefiniować te limity, aby odpowiedzieć na wszystkie żądania skierowane do API lub możesz ograniczyć zakres do każdej ścieżki URL lub czasownika i ścieżki HTTP.
Ustawiać
Instalacja NuGet :
Install-Package AspNetCoreRateLimit
Kod 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(); }
Oprogramowanie pośrednie należy zarejestrować przed innymi komponentami oprócz loggerFactory.
jeśli załadujesz saldo swojej aplikacji, będziesz musiał użyć IDistributedCache
z Redis lub SQLServer, aby wszystkie instancje pustułki miały ten sam magazyn limitów prędkości. Zamiast magazynów w pamięci należy wstrzykiwać rozproszone sklepy w następujący sposób:
// inject counter and rules distributed cache stores services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
Konfiguracja i ogólne zasady 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 } ] }
Jeśli EnableEndpointRateLimiting
ma wartość false
wówczas ograniczenia będą obowiązywać globalnie i będą obowiązywać tylko reguły, które mają jako punkt końcowy *
. Na przykład, jeśli ustawisz limit 5 połączeń na sekundę, każde połączenie HTTP do dowolnego punktu końcowego będzie wliczane do tego limitu.
Jeśli EnableEndpointRateLimiting
ma wartość true
wówczas limity będą obowiązywać dla każdego punktu końcowego, jak w {HTTP_Verb}{PATH}
. Na przykład, jeśli ustawisz limit 5 połączeń na sekundę dla *:/api/values
klient może wywoływać GET /api/values
5 razy na sekundę, ale także 5 razy PUT /api/values
.
Jeśli StackBlockedRequests
jest ustawiony na false
odrzucone wywołania, nie są dodawane do licznika przepustnicy. Jeśli klient wysyła 3 żądania na sekundę i ustawiłeś limit jednego połączenia na sekundę, inne limity, takie jak licznik minut lub dziennie, będą rejestrować tylko pierwsze połączenie, które nie zostało zablokowane. Jeśli chcesz, aby odrzucone żądania były wliczane do innych limitów, musisz ustawić StackBlockedRequests
na wartość true
.
RealIpHeader
służy do wyodrębnienia adresu IP klienta, gdy serwer Kestrel znajduje się za odwrotnym proxy, jeśli twój serwer proxy używa innego nagłówka, wówczas X-Real-IP
używa tej opcji, aby go skonfigurować.
ClientIdHeader
służy do wyodrębnienia identyfikatora klienta dla białej listy, jeśli identyfikator klienta jest obecny w tym nagłówku i jest zgodny z wartością określoną w ClientWhitelist, wówczas nie są stosowane żadne limity stawek.
Zastąp zasady ogólne dla określonych adresów 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 } ] } ] }
Pole IP obsługuje wartości i zakresy IP v4 i v6, takie jak „192.168.0.0/24”, „fe80 :: / 10” lub „192.168.0.0-192.168.0.255”.
Definiowanie reguł limitów stawek
Reguła składa się z punktu końcowego, okresu i limitu.
Format punktu końcowego to {HTTP_Verb}:{PATH}
, możesz kierować na dowolny czasownik HTTP za pomocą symbolu gwiazdki.
Format okresu to {INT}{PERIOD_TYPE}
, możesz użyć jednego z następujących typów okresów: s, m, h, d
.
Format limitu to {LONG}
.
Przykłady :
Ograniczenie prędkości wszystkich punktów końcowych do 2 połączeń na sekundę:
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
Jeśli z tego samego adresu IP w tej samej sekundzie wykonasz 3 połączenia GET z wartościami api / wartości, ostatnie połączenie zostanie zablokowane. Ale jeśli w tej samej sekundzie również wywołasz PUT api / wartości, żądanie zostanie zrealizowane, ponieważ jest to inny punkt końcowy. Gdy ograniczenie prędkości punktu końcowego jest włączone, każde połączenie jest ograniczone w oparciu o {HTTP_Verb}{PATH}
.
Ogranicz limit połączeń z dowolnym czasownikiem HTTP na /api/values
do 5 połączeń na 15 minut:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
Limit stawek ODBIERZ połączenie do /api/values
do 5 połączeń na godzinę:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
Jeśli z tego samego adresu IP w ciągu godziny wykonasz 6 wywołań GET do api / wartości, ostatnie połączenie zostanie zablokowane. Ale jeśli w tej samej godzinie również wywołasz GET api / wartości / 1, żądanie zostanie zrealizowane, ponieważ jest to inny punkt końcowy.
Zachowanie
Gdy klient wykonuje połączenie HTTP, IpRateLimitMiddleware wykonuje następujące czynności:
- wyodrębnia adres IP, identyfikator klienta, czasownik HTTP i adres URL z obiektu żądania, jeśli chcesz wdrożyć własną logikę wyodrębniania, możesz przesłonić
IpRateLimitMiddleware.SetIdentity
- szuka adresu IP, identyfikatora klienta i adresu URL na białej liście, jeśli są zgodne, wówczas nie są podejmowane żadne działania
- wyszukuje dopasowanie w regułach IP, wszystkie obowiązujące reguły są pogrupowane według okresu, dla każdego okresu stosowana jest najbardziej restrykcyjna reguła
- wyszukuje dopasowanie w regułach ogólnych, jeśli ogólna reguła, która pasuje, ma zdefiniowany okres, który nie jest obecny w regułach własności intelektualnej, wówczas używana jest również ta reguła ogólna
- dla każdej pasującej reguły licznik limitu prędkości jest zwiększany, jeśli wartość licznika jest większa niż limit reguły, wówczas żądanie zostaje zablokowane
Jeśli żądanie zostanie zablokowane, klient otrzyma odpowiedź tekstową w następujący sposób:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
Możesz dostosować odpowiedź, zmieniając te opcje HttpStatusCode
i QuotaExceededMessage
, jeśli chcesz zaimplementować własną odpowiedź, możesz przesłonić IpRateLimitMiddleware.ReturnQuotaExceededResponse
. Wartość nagłówka Retry-After
jest wyrażana w sekundach.
Jeśli żądanie nie ma ograniczonej prędkości, wówczas do skomponowania nagłówków X-Rate-Limit wykorzystywany jest najdłuższy okres zdefiniowany w regułach dopasowywania, nagłówki te są wstrzykiwane w odpowiedzi:
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
Domyślnie zablokowane żądanie jest rejestrowane za pomocą Microsoft.Extensions.Logging.ILogger
, jeśli chcesz wdrożyć własne rejestrowanie, możesz zastąpić IpRateLimitMiddleware.LogBlockedRequest
. Domyślny program rejestrujący emituje następujące informacje, gdy żądanie ma ograniczoną szybkość:
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.
Aktualizuj limity częstości w czasie wykonywania
Podczas uruchamiania aplikacji reguły limitu szybkości IP zdefiniowane w appsettings.json
są ładowane do pamięci podręcznej przez MemoryCacheClientPolicyStore
lub DistributedCacheIpPolicyStore
zależności od używanego dostawcy pamięci podręcznej. Możesz uzyskać dostęp do magazynu zasad IP w kontrolerze i zmodyfikować reguły IP w następujący sposób:
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); } }
W ten sposób możesz przechowywać limity prędkości IP w bazie danych i wypychać je w pamięci podręcznej po każdym uruchomieniu aplikacji.
Ograniczenie stawki na podstawie identyfikatora klienta
Dzięki oprogramowaniu pośredniczącemu ClientRateLimit możesz ustawić wiele limitów dla różnych scenariuszy, np. Zezwalając klientowi na wykonywanie maksymalnej liczby połączeń w interwale czasowym, takim jak sekunda, 15 minut itp. Możesz zdefiniować te limity, aby odpowiedzieć na wszystkie żądania wysłane do API lub może ograniczyć ograniczenia do każdej ścieżki URL lub czasownika i ścieżki HTTP.
Ustawiać
Instalacja NuGet :
Install-Package AspNetCoreRateLimit
Kod 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(); }
Oprogramowanie pośrednie należy zarejestrować przed innymi komponentami oprócz loggerFactory.
jeśli załadujesz saldo swojej aplikacji, będziesz musiał użyć IDistributedCache
z Redis lub SQLServer, aby wszystkie instancje pustułki miały ten sam magazyn limitów prędkości. Zamiast magazynów w pamięci należy wstrzykiwać rozproszone sklepy w następujący sposób:
// inject counter and rules distributed cache stores services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
Konfiguracja i ogólne zasady 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 } ] }
Jeśli EnableEndpointRateLimiting
ma wartość false
wówczas ograniczenia będą obowiązywać globalnie i będą obowiązywać tylko reguły, które mają jako punkt końcowy *
. Na przykład, jeśli ustawisz limit 5 połączeń na sekundę, każde połączenie HTTP do dowolnego punktu końcowego będzie wliczane do tego limitu.
Jeśli EnableEndpointRateLimiting
ma wartość true
wówczas limity będą obowiązywać dla każdego punktu końcowego, jak w {HTTP_Verb}{PATH}
. Na przykład, jeśli ustawisz limit 5 połączeń na sekundę dla *:/api/values
klient może wywoływać GET /api/values
5 razy na sekundę, ale także 5 razy PUT /api/values
.
Jeśli StackBlockedRequests
jest ustawiony na false
odrzucone wywołania, nie są dodawane do licznika przepustnicy. Jeśli klient wysyła 3 żądania na sekundę i ustawiłeś limit jednego połączenia na sekundę, inne limity, takie jak licznik minut lub dziennie, będą rejestrować tylko pierwsze połączenie, które nie zostało zablokowane. Jeśli chcesz, aby odrzucone żądania były wliczane do innych limitów, musisz ustawić StackBlockedRequests
na wartość true
.
ClientIdHeader
służy do wyodrębnienia identyfikatora klienta, jeśli identyfikator klienta jest obecny w tym nagłówku i jest zgodny z wartością określoną w ClientWhitelist, wówczas nie są stosowane żadne ograniczenia szybkości.
Zastąp zasady ogólne dla określonych klientów 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 } ] } ] }
Definiowanie reguł limitów stawek
Reguła składa się z punktu końcowego, okresu i limitu.
Format punktu końcowego to {HTTP_Verb}:{PATH}
, możesz kierować na dowolny czasownik HTTP za pomocą symbolu gwiazdki.
Format okresu to {INT}{PERIOD_TYPE}
, możesz użyć jednego z następujących typów okresów: s, m, h, d
.
Format limitu to {LONG}
.
Przykłady :
Ograniczenie prędkości wszystkich punktów końcowych do 2 połączeń na sekundę:
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
Jeśli w tej samej sekundzie klient wykona 3 wywołania GET do api / wartości, ostatnie połączenie zostanie zablokowane. Ale jeśli w tej samej sekundzie wywoła również PUT api / wartości, żądanie zostanie zrealizowane, ponieważ jest to inny punkt końcowy. Gdy ograniczenie prędkości punktu końcowego jest włączone, każde połączenie jest ograniczone w oparciu o {HTTP_Verb}{PATH}
.
Ogranicz limit połączeń z dowolnym czasownikiem HTTP na /api/values
do 5 połączeń na 15 minut:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
Limit stawek ODBIERZ połączenie do /api/values
do 5 połączeń na godzinę:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
Jeśli w ciągu godziny klient wykona 6 wywołań GET do api / wartości, ostatnie połączenie zostanie zablokowane. Ale jeśli w tej samej godzinie również wywoła GET api / values / 1, żądanie zostanie zrealizowane, ponieważ jest to inny punkt końcowy.
Zachowanie
Gdy klient wykonuje połączenie HTTP, ClientRateLimitMiddleware wykonuje następujące czynności:
- wyodrębnia identyfikator klienta, czasownik HTTP i adres URL z obiektu żądania, jeśli chcesz wdrożyć własną logikę wyodrębniania, możesz zastąpić
ClientRateLimitMiddleware.SetIdentity
- wyszukuje identyfikator klienta i adres URL na białej liście, jeśli są zgodne, wówczas nie są podejmowane żadne działania
- wyszukuje dopasowanie w regułach klienta, wszystkie obowiązujące reguły są pogrupowane według okresu, dla każdego okresu stosowana jest najbardziej restrykcyjna reguła
- wyszukuje dopasowanie w regułach ogólnych, jeśli ogólna reguła, która pasuje, ma zdefiniowany okres, którego nie ma w regułach klienta, wówczas używana jest również ta reguła ogólna
- dla każdej pasującej reguły licznik limitu prędkości jest zwiększany, jeśli wartość licznika jest większa niż limit reguły, wówczas żądanie zostaje zablokowane
Jeśli żądanie zostanie zablokowane, klient otrzyma odpowiedź tekstową w następujący sposób:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
Możesz dostosować odpowiedź, zmieniając te opcje HttpStatusCode
i QuotaExceededMessage
, jeśli chcesz zaimplementować własną odpowiedź, możesz przesłonić ClientRateLimitMiddleware.ReturnQuotaExceededResponse
. Wartość nagłówka Retry-After
jest wyrażana w sekundach.
Jeśli żądanie nie ma ograniczonej prędkości, wówczas do skomponowania nagłówków X-Rate-Limit wykorzystywany jest najdłuższy okres zdefiniowany w regułach dopasowywania, nagłówki te są wstrzykiwane w odpowiedzi:
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
Domyślnie zablokowane żądanie jest rejestrowane za pomocą Microsoft.Extensions.Logging.ILogger
, jeśli chcesz wdrożyć własne rejestrowanie, możesz zastąpić ClientRateLimitMiddleware.LogBlockedRequest
. Domyślny program rejestrujący emituje następujące informacje, gdy żądanie ma ograniczoną szybkość:
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.
Aktualizuj limity częstości w czasie wykonywania
Podczas uruchamiania aplikacji reguły limitów prędkości klienta zdefiniowane w appsettings.json
są ładowane do pamięci podręcznej przez MemoryCacheClientPolicyStore
lub DistributedCacheClientPolicyStore
zależności od używanego dostawcy pamięci podręcznej. Możesz uzyskać dostęp do magazynu zasad klienta w kontrolerze i zmodyfikować reguły w następujący sposób:
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); } }
W ten sposób możesz przechowywać limity stawek klienta w bazie danych i przesyłać je do pamięci podręcznej po każdym uruchomieniu aplikacji.