サーチ…
備考
AspNetCoreRateLimitは、クライアントがIPアドレスまたはクライアントIDに基づいてWeb APIまたはMVCアプリケーションに要求できる割合を制御するように設計されたオープンソースのASP.NET コアレート制限ソリューションです。
クライアントIPに基づくレート制限
IpRateLimitミドルウェアを使用すると、IPまたはIP範囲で1秒、15分などの時間間隔で最大数のコールを作成できるように、さまざまなシナリオに複数の制限を設定できます。これらの制限を定義して、 APIを使用するか、各URLパスまたはHTTP動詞とパスの範囲を指定できます。
セットアップ
NuGetインストール :
Install-Package AspNetCoreRateLimit
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(); }
loggerFactory以外のコンポーネントの前にミドルウェアを登録する必要があります。
アプリケーションの負荷を分散する場合は、 IDistributedCache
またはSQLServerでIDistributedCache
を使用して、すべてのkestrelインスタンスが同じレート制限ストアを持つようにする必要があります。インメモリストアの代わりに、次のように分散ストアを注入する必要があります。
// inject counter and rules distributed cache stores services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
構成と一般規則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 } ] }
EnableEndpointRateLimiting
がfalse
設定されてfalse
場合、制限はグローバルに適用され、エンドポイントとして持つルールのみ*
が適用されます。たとえば、毎秒5コールの制限を設定すると、任意のエンドポイントへのHTTPコールはその制限にカウントされます。
EnableEndpointRateLimiting
がtrue
設定されているtrue
、 {HTTP_Verb}{PATH}
ように各エンドポイントに制限が適用されます。たとえば、 *:/api/values
1秒あたり5コールの制限を設定した*:/api/values
、クライアントはGET /api/values
1秒間に5回だけでなく5回PUT /api/values
呼び出すことができPUT /api/values
。
StackBlockedRequests
がfalse
設定されている場合、拒否されたコールはスロットルカウンタに追加されません。クライアントが1秒あたり3回の要求を行い、毎秒1通の制限を設定した場合、1分あたりまたは1日あたりのカウンタなどの他の制限は、ブロックされなかった最初の通話のみを記録します。拒否された要求を他の制限にカウントする場合は、 StackBlockedRequests
をtrue
に設定するStackBlockedRequests
がありtrue
。
RealIpHeader
は、Kestrelサーバーがリバースプロキシの背後にあるときにクライアントIPを抽出するために使用されます。プロキシが別のヘッダーを使用する場合、 X-Real-IP
はこのオプションを使用して設定します。
ClientIdHeader
を使用してホワイトリストのクライアントIDを抽出します。クライアントIDがこのヘッダーにあり、ClientWhitelistで指定された値と一致する場合、レート制限は適用されません。
特定の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 } ] } ] }
IPフィールドは、IP v4とv6の値と範囲をサポートしています。たとえば、「192.168.0.0/24」、「fe80 :: / 10」、「192.168.0.0-192.168.0.255」です。
レート制限ルールの定義
ルールは、エンドポイント、期間、および制限で構成されます。
エンドポイント形式は{HTTP_Verb}:{PATH}
です。アスタリスク記号を使用して任意のHTTP動詞をターゲットにすることができます。
期間形式は{INT}{PERIOD_TYPE}
です。次の期間タイプのいずれかを使用できます: s, m, h, d
。
制限フォーマットは{LONG}
です。
例 :
レートは、すべてのエンドポイントを毎秒2コールに制限します。
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
同じIPから同じ秒間に3つのGET呼び出しをapi /値にすると、最後の呼び出しはブロックされます。しかし、同じ秒でPUT api / valuesを呼び出しても、別のエンドポイントであるため、要求は完了します。エンドポイントレート制限が有効になっている場合、各コールは{HTTP_Verb}{PATH}
基づいてレート制限されます。
任意のHTTP動詞を/api/values
に設定してレート制限のコールを15分間に5コールに制限する:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
レート制限/api/values
へのGETコールを1時間当たり5コールにする:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
同じIPから、1時間後にAPI /値を6回GETした場合、最後の呼び出しはブロックされます。しかし、同じ時間にGET api / values / 1を呼び出しても、要求が異なるエンドポイントであるため、要求は完了します。
動作
クライアントがHTTP呼び出しを行うと、IpRateLimitMiddlewareは次のことを行います。
- 要求オブジェクトからIP、クライアントID、HTTP動詞およびURLを抽出します。独自の抽出ロジックを実装する場合は、
IpRateLimitMiddleware.SetIdentity
をオーバーライドできます - ホワイトリスト内のIP、クライアントID、URLを検索し、一致するものがあればアクションは実行されません
- 一致するかどうかをIPルールで検索し、適用されるすべてのルールは、期間ごとにグループ化されます。最も期間の厳しいルールが使用されます
- 一般ルールで一致するものを検索し、一致する一般ルールに定義された期間がIPルールに存在しない場合、この一般ルールも使用されます
- 各マッチングルールについて、レート制限カウンタがインクリメントされ、カウンタ値がルール制限より大きい場合、要求がブロックされる
要求がブロックされると、クライアントは次のようなテキスト応答を受け取ります。
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
これらのオプションHttpStatusCode
とQuotaExceededMessage
変更して応答をカスタマイズすることができます。独自の応答を実装する場合は、 IpRateLimitMiddleware.ReturnQuotaExceededResponse
をオーバーライドできます。 Retry-After
ヘッダー値は秒単位で表されます。
リクエストがレート制限を受けない場合、マッチするルールで定義されている最長期間がX-Rate-Limitヘッダーを構成するために使用され、これらのヘッダーはレスポンスに挿入されます。
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
既定でブロックされた要求はMicrosoft.Extensions.Logging.ILogger
を使用して記録されます。独自のログを実装する場合は、 IpRateLimitMiddleware.LogBlockedRequest
をオーバーライドできます。デフォルトのロガーは、リクエストがレート制限を受けると、次の情報を出力します。
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.
実行時のレート制限の更新
アプリケーションの起動時に、 appsettings.json
定義されているIPレート制限ルールは、使用しているキャッシュ・プロバイダのタイプに応じて、 MemoryCacheClientPolicyStore
またはDistributedCacheIpPolicyStore
によってキャッシュにロードされます。コントローラ内のIPポリシーストアにアクセスし、IPルールを次のように変更できます。
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); } }
こうすることで、データベースにIPレートの制限を格納し、各アプリケーションの起動後にキャッシュにプッシュすることができます。
クライアントIDに基づくレート制限
ClientRateLimitミドルウェアでは、クライアントが毎秒、15分などの時間間隔で最大数のコールを行うことを許可するなど、さまざまなシナリオに対して複数の制限を設定できます。これらの制限を定義して、それぞれのURLパスまたはHTTP動詞とパスの範囲を絞り込むことができます。
セットアップ
NuGetインストール :
Install-Package AspNetCoreRateLimit
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(); }
loggerFactory以外のコンポーネントの前にミドルウェアを登録する必要があります。
アプリケーションの負荷を分散する場合は、 IDistributedCache
またはSQLServerでIDistributedCache
を使用して、すべてのkestrelインスタンスが同じレート制限ストアを持つようにする必要があります。インメモリストアの代わりに、次のように分散ストアを注入する必要があります。
// inject counter and rules distributed cache stores services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>(); services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
構成と一般規則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 } ] }
EnableEndpointRateLimiting
がfalse
設定されてfalse
場合、制限はグローバルに適用され、エンドポイントとして持つルールのみ*
が適用されます。たとえば、毎秒5コールの制限を設定すると、任意のエンドポイントへのHTTPコールはその制限にカウントされます。
EnableEndpointRateLimiting
がtrue
設定されているtrue
、 {HTTP_Verb}{PATH}
ように各エンドポイントに制限が適用されます。たとえば、 *:/api/values
1秒あたり5コールの制限を設定した*:/api/values
、クライアントはGET /api/values
1秒間に5回だけでなく5回PUT /api/values
呼び出すことができPUT /api/values
。
StackBlockedRequests
がfalse
設定されている場合、拒否されたコールはスロットルカウンタに追加されません。クライアントが1秒あたり3回の要求を行い、毎秒1通の制限を設定した場合、1分あたりまたは1日あたりのカウンタなどの他の制限は、ブロックされなかった最初の通話のみを記録します。拒否された要求を他の制限にカウントする場合は、 StackBlockedRequests
をtrue
に設定するStackBlockedRequests
がありtrue
。
ClientIdHeader
を使用してクライアントIDを抽出します。クライアントIDがこのヘッダーにあり、ClientWhitelistで指定された値と一致する場合、レート制限は適用されません。
特定のクライアントの一般的なルールを上書きする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 } ] } ] }
レート制限ルールの定義
ルールは、エンドポイント、期間、および制限で構成されます。
エンドポイント形式は{HTTP_Verb}:{PATH}
です。アスタリスク記号を使用して任意のHTTP動詞をターゲットにすることができます。
期間形式は{INT}{PERIOD_TYPE}
です。次の期間タイプのいずれかを使用できます: s, m, h, d
。
制限フォーマットは{LONG}
です。
例 :
レートは、すべてのエンドポイントを毎秒2コールに制限します。
{ "Endpoint": "*", "Period": "1s", "Limit": 2 }
同じ秒でクライアントが3つのGET呼び出しをapi /値にすると、最後の呼び出しはブロックされます。しかし、同じ秒でPUT api / valuesも呼び出すと、別のエンドポイントであるため、要求は完了します。エンドポイントレート制限が有効になっている場合、各コールは{HTTP_Verb}{PATH}
基づいてレート制限されます。
任意のHTTP動詞を/api/values
に設定してレート制限のコールを15分間に5コールに制限する:
{ "Endpoint": "*:/api/values", "Period": "15m", "Limit": 5 }
レート制限/api/values
へのGETコールを1時間当たり5コールにする:
{ "Endpoint": "get:/api/values", "Period": "1h", "Limit": 5 }
1時間後にクライアントがAPI /値に対して6回GET呼び出しを行うと、最後の呼び出しはブロックされます。しかし、同じ時間にGET api / values / 1を呼び出すと、別のエンドポイントであるため、要求は完了します。
動作
クライアントがHTTP呼び出しを行うと、ClientRateLimitMiddlewareは次のことを行います。
- 要求オブジェクトからClient id、HTTP verb、およびURLを抽出します。独自の抽出ロジックを実装する場合は、
ClientRateLimitMiddleware.SetIdentity
をオーバーライドできます - ホワイトリストのクライアントIDとURLを検索し、一致するものがあればアクションは実行されません
- クライアントのルールで一致を検索し、適用されるすべてのルールは、期間ごとにグループ化されます。最も制限の厳しいルールが使用されます
- 一般ルールで一致するものを検索します。一致する一般ルールに定義された期間がクライアントルールに存在しない場合、この一般ルールも使用されます
- 各マッチングルールについて、レート制限カウンタがインクリメントされ、カウンタ値がルール制限より大きい場合、要求がブロックされる
要求がブロックされると、クライアントは次のようなテキスト応答を受け取ります。
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
これらのオプションHttpStatusCode
とQuotaExceededMessage
変更してレスポンスをカスタマイズすることができます。独自のレスポンスを実装する場合は、 ClientRateLimitMiddleware.ReturnQuotaExceededResponse
をオーバーライドできます。 Retry-After
ヘッダー値は秒単位で表されます。
リクエストがレート制限を受けない場合、マッチするルールで定義されている最長期間がX-Rate-Limitヘッダーを構成するために使用され、これらのヘッダーはレスポンスに挿入されます。
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
既定でブロックされた要求はMicrosoft.Extensions.Logging.ILogger
を使用して記録されます。独自のログを実装する場合は、 ClientRateLimitMiddleware.LogBlockedRequest
をオーバーライドできます。デフォルトのロガーは、リクエストがレート制限を受けると、次の情報を出力します。
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.
実行時のレート制限の更新
アプリケーションの起動時に、 appsettings.json
定義されたクライアントレート制限ルールは、使用しているキャッシュプロバイダのタイプに応じて、 MemoryCacheClientPolicyStore
またはDistributedCacheClientPolicyStore
によってキャッシュにロードされます。コントローラ内のクライアントポリシーストアにアクセスし、ルールを次のように変更できます。
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); } }
この方法で、データベースにクライアントレート制限を格納し、各アプリケーションの起動後にキャッシュにプッシュすることができます。