サーチ…


備考

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
      }
    ]
  }

EnableEndpointRateLimitingfalse設定されてfalse場合、制限はグローバルに適用され、エンドポイントとして持つルールのみ*が適用されます。たとえば、毎秒5コールの制限を設定すると、任意のエンドポイントへのHTTPコールはその制限にカウントされます。

EnableEndpointRateLimitingtrue設定されているtrue{HTTP_Verb}{PATH}ように各エンドポイントに制限が適用されます。たとえば、 *:/api/values 1秒あたり5コールの制限を設定した*:/api/values 、クライアントはGET /api/values 1秒間に5回だけでなく5回PUT /api/values呼び出すことができPUT /api/values

StackBlockedRequestsfalse設定されている場合、拒否されたコールはスロットルカウンタに追加されません。クライアントが1秒あたり3回の要求を行い、毎秒1通の制限を設定した場合、1分あたりまたは1日あたりのカウンタなどの他の制限は、ブロックされなかった最初の通話のみを記録します。拒否された要求を他の制限にカウントする場合は、 StackBlockedRequeststrueに設定する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.

これらのオプションHttpStatusCodeQuotaExceededMessage変更して応答をカスタマイズすることができます。独自の応答を実装する場合は、 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
      }
    ]
  }

EnableEndpointRateLimitingfalse設定されてfalse場合、制限はグローバルに適用され、エンドポイントとして持つルールのみ*が適用されます。たとえば、毎秒5コールの制限を設定すると、任意のエンドポイントへのHTTPコールはその制限にカウントされます。

EnableEndpointRateLimitingtrue設定されているtrue{HTTP_Verb}{PATH}ように各エンドポイントに制限が適用されます。たとえば、 *:/api/values 1秒あたり5コールの制限を設定した*:/api/values 、クライアントはGET /api/values 1秒間に5回だけでなく5回PUT /api/values呼び出すことができPUT /api/values

StackBlockedRequestsfalse設定されている場合、拒否されたコールはスロットルカウンタに追加されません。クライアントが1秒あたり3回の要求を行い、毎秒1通の制限を設定した場合、1分あたりまたは1日あたりのカウンタなどの他の制限は、ブロックされなかった最初の通話のみを記録します。拒否された要求を他の制限にカウントする場合は、 StackBlockedRequeststrueに設定する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.

これらのオプションHttpStatusCodeQuotaExceededMessage変更してレスポンスをカスタマイズすることができます。独自のレスポンスを実装する場合は、 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);
    }
}

この方法で、データベースにクライアントレート制限を格納し、各アプリケーションの起動後にキャッシュにプッシュすることができます。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow