只有累積,沒有奇蹟

2022年5月9日 星期一

[NETCore] ASP.NET Core 限流框架 AspNetCoreRateLimit

前言
開發者都知道系統上線後才是挑戰的開始,舉例來說像是每天不斷的有爬蟲程式來抓取網站資料,或是對外開放的 API 服務遭到攻擊事件,若沒有良好的防範機制很有可能造成 Server 因為攻擊無法正常服務,甚至引起雪崩效應影響到其他系統服務,在 ASP.NET Core 中可以透過  AspNetCoreRateLimit  框架根據 Request 的 IP 或是 ClientID 來達到限制流量的效果,這篇就來簡單分享一下有關 AspNetCoreRateLimit 的安裝與基本使用,若有問題或是錯誤的地方歡迎網路的高手大大給予指導

安裝
首先,為了方便大家更容易了解,建立一個 ASP.NET Core Web Application 應用程式來作範例,接著開啟 Nuget Package Mnage 輸入 AspNetCoreRateLimit 搜尋,安裝目前最新版的 AspNetCoreRateLimit 套件
  或是透過 Nuget Package Console 輸入下列指令
Install-Package AspNetCoreRateLimit -Version 3.0.5
安裝完畢之後到專案檔底下確認是否有安裝成功

使用與設定 
AspNetCoreRateLimit 可以根據 IP 或是客戶 ID 來作為限速依據,也可以指定 API 中某一個對外接口或是 Http Method 分別設定其限制,兩者皆是在中間件 ( Middleware) 判斷其請求率是否達到設定的上限,分別是  IpRateLimitMiddleware  與  ClientRateLimitMiddleware ,再來決定是否要讓此 Request 繼續下一步或是返回的動作,這裡以限制 IP 為範例,安裝完後接下來就是到 start.cs 中加入下列代碼
    public void ConfigureServices(IServiceCollection services)
    {
        // 將速限計數器資料儲存在 Memory 中
        services.AddMemoryCache();

        // 從 appsettings.json 讀取 IpRateLimiting 設定 
        services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));

        // 從 appsettings.json 讀取 Ip Rule 設定
        services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));

        // 注入 counter and IP Rules 
        services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
        services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();

        // Add framework services.
        services.AddMvc();

        // the clientId/clientIp resolvers use it.
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

        // Rate Limit configuration 設定
        services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseIpRateLimiting();
        app.UseMvc();
    }
代碼中的 Code 都有加上註解跟說明,這裡就不在詳細說明,在上述代碼中可以看到 Policy 與 IP Rule 都是從 appSettings.json 設定檔中取得,因此下一步就是在設定檔中加上限流相關的設定資訊

IpRateLimiting
"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
      }
    ]
  }
IpRateLimiting 區塊設定 :  
  • EnableEndpointRateLimiting : 全部套用為 false,設定為 true 則 適用於每個 endPoint 。
  • StackBlockedRequests : false 代表拒絕的呼叫不會加入計數器中,如果希望被拒絕的請求也要包含在計數器中則要設定為 true。  
  • ClientIdHeader : 如果 Request 請求中的 Head 客戶 ID 與 ClientWhitelist 相同的話就不受限流設定影響。
  • GeneralRules : Period 設定可以包含天(d)、小時(h)、分鐘(m) 以及秒(s) 等設定值,舉例來說第一個區段設定限制為 1 秒限定 2 次,超過就會開始限流。

  • IpRateLimitPolicics
    在 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 支援 IPv4 & IPv6 

    限流規則
    使用 Throttling 最重要的是了解限速的規則設定,才可以因應不同的情境調整需要的限速(流)設定,這裡直接來看一下官方說明文件提到的 Throttling 規則與 Rule,有 EndPoint Format、Period 與 Limit 等三種格式
    廢話不多說直接看範例更容易了解
    Sample 1 : 
    {
        "Endpoint": "*",
        "Period": "1s",
        "Limit": 2
    }
    目的 : 所有端點每秒鐘允許 2 次呼叫 
    使用 GET 方法發送 api / Values 一秒三個 Request,前 2 個 Request 會 pass,第 3 個 request 會無法使用,如果同時間使用 PUT 方法呼叫相同 api / values 則會 pass,因為 GET 與 PUT 分別加總不會混和計算。

     Sample 2 : 
    {
        "Endpoint": "*:/api/values",
        "Period": "15m",
        "Limit": 5
    }
    目的 : api / Values 用 HTTP 所有 Method 的設定為每 15 分鐘允許呼叫 5 次 

    Sample 3 : 
    {
        "Endpoint": "get:/api/values",
        "Period": "1h",
        "Limit": 5
    }
    目的 : api / Values 用 HTTP GET Method 的設定為每小時允許呼叫 5 次
    每小時呼叫 5 次,PASS 
    每小時呼叫 6 次,第六次則會正常取得正確資料

    當超過我們定義的次數限制時會顯示
    API calls quota exceeded! maximum admitted 2 per 10s.
    打開 Chrome 瀏覽器透過 Network 可以看到回傳的內容細節
    可以從開發者工具看到 Response 的資訊,在 Response Header 中 StatusCode 為 429,retry-after 為 9,如果不希望在 Response Header 中顯示 retry 次數,可以在 appsettings.json 設定檔中加上 DisableRateLimitHeaders 為 true 隱藏 retry 資訊,如果想要自訂 Response 時的訊息內容可以參考官網的說明 : 傳送門


    感想
    除了在 ASP.NET Core 有限流以外,作者也有提供 .NET Framework 版本的限流框架 WebApiThrottle,可以省下自己開發時間,也可以快速地將限流機制套用在需要的專案上,另外在本篇中提到的都是以 IP 為出發作為限制,在 AspNetCoreRateLimit 也有提供使用 Client 作為設定的機制,如有需要可以參考 ClientRateLimitMiddleware 相關說明,希望這篇介紹可以有幫助到有需要的朋友,謝謝

    參考
    AspNetCoreRateLimit
    Throttling your API in ASP.NET

    0 意見:

    張貼留言

    Copyright © m@rcus 學習筆記 | Powered by Blogger

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com