I need a way in C# / .NET Core app to limit the number of queries that can be made to a public endpoint within a certain amount of time. This is to prevent a brute force attack.
For example, a user makes a request to endpoint xx and it should allow only 3 requests, rejecting the fourth. How can I achieve this if it's a public endpoint where I cannot identify who is making the request?
You can achieve the rate limiting via the nuget AspNetCoreRateLimit
by implementing a middleware service.
First you need to add the package into your project via :
dotnet add package AspNetCoreRateLimit
or use install this package via manage nuget packages UI interface in Visual Studio.
Secondly, update your appsettings.json
file to use configuration like this:
{
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"GeneralRules": [
{
"Endpoint": "*:/api/publicendpoint",
"Period": "1m",
"Limit": 3
}
]
},
"IpRateLimitPolicies": {
"IpRules": []
}
}
Thirdly, register rate limiting services in your Startup.cs
or Program.cs
like this:
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting")); // Load configurations added from appsettings.json
services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));
services.AddMemoryCache(); // Specify memory cache to store count limits
services.AddInMemoryRateLimiting(); // Add rate limiting services
services.AddMvc();
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>(); // Specify IpRateLimit middleware as single instance
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseIpRateLimiting(); // Enable IpRateLimiting middleware which we will implement in fourth stage
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Fourthly, implement the middleware to limit the number of request like this:
public class RateLimitingMiddleware
{
private readonly RequestDelegate _next;
private static readonly MemoryCache _memoryCache = new MemoryCache(new MemoryCacheOptions());
private static readonly TimeSpan _timeWindow = TimeSpan.FromMinutes(1);
private static readonly int _maxRequests = 3;
public RateLimitingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var ipAddress = context.Connection.RemoteIpAddress.ToString();
if (IsRateLimited(ipAddress))
{
context.Response.StatusCode = 429;
await context.Response.WriteAsync("Too many requests. Please try again later.");
return;
}
await _next(context);
}
private bool IsRateLimited(string ipAddress)
{
var cacheKey = $"RateLimit_{ipAddress}";
if (_memoryCache.TryGetValue(cacheKey, out int requestCount))
{
if (requestCount >= _maxRequests)
{
return true;
}
_memoryCache.Set(cacheKey, ++requestCount, _timeWindow);
}
else
{
_memoryCache.Set(cacheKey, 1, _timeWindow);
}
return false;
}
}
public static class RateLimitingMiddlewareExtensions
{
public static IApplicationBuilder UseRateLimiting(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RateLimitingMiddleware>();
}
}
Now you can specify the and tell your application to use in your Startup.cs
or Program.cs
as shown here:
app.UseRateLimiting();