在网站或API应用中,我们为了防止无聊人士或恶意攻击,通常希望屏蔽某一IP短时间的内高频率请求。在ASP.NET Core中,限制IP请求频率非常简单,我们来看看吧。
轮子一个
.NET Core 目前的生态发展十分迅猛,轮子也越来越多。只要轮子不爆胎,本来就不需要996的.NET开发者就能继续10 5 5!这不,为了限制IP请求频率,我找到了一个不错的轮子:
AspNetCoreRateLimit
GitHub链接:https://github.com/stefanprodan/AspNetCoreRateLimit
安装轮子
我的应用目前一个ASP.NET Core 2.2 MVC的网站,我们可以通过NuGet安装这个轮子,截至本文,它的最新版是3.0.5。
Install-Package AspNetCoreRateLimit
或 .NET Core CLI
dotnet add package AspNetCoreRateLimit
修改Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// 需要从appsettings.json中加载配置
services.AddOptions();
// 存储IP计数器及配置规则
services.AddMemoryCache();
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
// 按照文档,这两个是3.x版的breaking change,要加上
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
}
以及
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// 注意顺序,放在 UseMvc 上面
app.UseIpRateLimiting();
app.UseMvc();
}
配置轮子
我的网站有一个URL(/fw/{token}),我希望限制1分钟内一个IP最多访问30次。但是对于其他URL,我并不想做任何限制。
[Route("/fw/{token}")]
public async Task<IActionResult> Forward(string token)
在 appsettings.json 里加入
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"GeneralRules": [
{
"Endpoint": "*:/fw/*",
"Period": "1m",
"Limit": 30
}
]
}
EnableEndpointRateLimiting设置为true,意思是IP限制会应用于单个配置的Endpoint上。如果是false的话,只会限制所有 * 的规则,而不能达到针对单个Endpoint配置的目的。
HttpStatusCode设置为429,意思是触发限制之后给客户端返回的HTTP状态码。
GeneralRules里我只配置了一条,针对/fw这URL的限制。其中,开头的 *: 表示任何HTTP VERB,如GET/POST,而结尾的 /* 表示需要考虑/fw后面的参数,也就是我MVC Action参数里的route参数。
针对不同token,会有不同的计数。比如IP为127.0.0.1的用户在1分钟内请求了 /fw/abcd 10次,又请求了 /fw/qwer 25次,也请求了 /fw/996icu 32次。那么对于该用户,/fw/abcd 的机会还剩下20次,/fw/qwer 的机会还剩下5次,而 /fw/996icu 在第31次请求时会返回429。
这里一定要注意,对于有参数的URL,如果不加结尾的 /* 那么轮子就会爆胎,并且把.NET程序员炸进ICU!
测试轮子
我们可以通过浏览器或CRUL测试IP限制。为了方便测试,我暂时把1分钟的请求频率限制为3次。
第一次请求 https://localhost:5001/fw/某token:
会发现服务器返回的header里多了3个东西:
X-Rate-Limit-Limit: 1m,表示该限制是1分钟以内
X-Rate-Limit-Remaining: 2,表示当前还剩2次机会
X-Rate-Limit-Reset 表示限制的重置时间
而1分钟内第三次访问该URL,就会触发限制,并且返回429
更多高级配置
AspNetCoreRateLimit 还有许多更高级的用法。比如针对Client ID而不是IP做限制、白名单、分布式计数器存储、自定义返回内容等等,可以参见官网文档:
https://github.com/stefanprodan/AspNetCoreRateLimit/wiki/IpRateLimitMiddleware#setup