Using Rate Limiting in ASP.NET Core 7 Web API by IP address
Asked Answered
E

1

6

There is currently a NuGet package that manages rate limiting by IP address called AspNetCoreRateLimit. However, .NET 7 introduced its own version of rate limiting and I wanted to use this instead as it is published by MS. I have not been able to find a good example that imitates this third party package by limiting by IP address. The code I put together is as follows:

builder.Services.AddRateLimiter(options =>
{
    options.RejectionStatusCode = 429;
    options.AddPolicy("api", httpContext =>
    {
        var IpAddress = httpContext.Connection.RemoteIpAddress.ToString();

        if (IpAddress != null)
        {
            return RateLimitPartition.GetFixedWindowLimiter(httpContext.Connection.RemoteIpAddress.ToString(),
            partition => new FixedWindowRateLimiterOptions
            {
                AutoReplenishment = true,
                PermitLimit = 5,
                Window = TimeSpan.FromMinutes(1)
            });
        }
        else
        {
            return RateLimitPartition.GetNoLimiter("");
        }
    });

});

However, the issue I am getting is a warning "Warning CS8602: Dereference of a possibly null reference." which I assume is because RemoteIpAddress could be null. I am curious if there is a better way to implement this IP rate limiting using this new .NET 7 library. If it matters, I am planning to host this web api in Azure app services (windows) and it is accessed by a SPA also hosted in an app service.

Eserine answered 6/2, 2023 at 19:42 Comment(4)
You should be able to accomplish this with a partitioned rate limiter using IP as the partition, as said in the comments in the official blog. Or you can achieve it using a weight http header like in this link. Not sure if this would be a good approach, but hope this helps.Hacking
In addition, this warning does not seem to affect its use.Hacking
@Hacking Yea I have read those comments previously. My code is pretty much there but I am struggling at getting the IP Address. Atleast in my localhost testing environment it does not seem to work.Eserine
Do you mean that the IP address cannot be obtained? Have you tried specifying IpAddress as a fixed IP to test to see if it works?Hacking
P
2

As you mentioned, the warning about the deference of a possibly null reference is not actually an error. It does come from this line:

httpContext.Connection.RemoteIpAddress.ToString()

Because the RemoteIpAddress property can be null (and therefore your ToString() call would throw).

However, this is (in my opinion), unrelated to your actual question, which was: " I am curious if there is a better way to implement this IP rate limiting using this new .NET 7 library"

There is an example of this directly in the Microsoft documentation: https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit?view=aspnetcore-7.0#limiter-with-onrejected-retryafter-and-globallimiter

Specifically, this code can be found on that page:

limiterOptions.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, IPAddress>(context =>
{
    IPAddress? remoteIpAddress = context.Connection.RemoteIpAddress;

    if (!IPAddress.IsLoopback(remoteIpAddress!))
    {
        return RateLimitPartition.GetTokenBucketLimiter
        (remoteIpAddress!, _ =>
            new TokenBucketRateLimiterOptions
            {
                TokenLimit = myOptions.TokenLimit2,
                QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
                QueueLimit = myOptions.QueueLimit,
                ReplenishmentPeriod = TimeSpan.FromSeconds(myOptions.ReplenishmentPeriod),
                TokensPerPeriod = myOptions.TokensPerPeriod,
                AutoReplenishment = myOptions.AutoReplenishment
            });
    }

    return RateLimitPartition.GetNoLimiter(IPAddress.Loopback);
});

This particular code is setting up a global limiter (sort of besides the point), but if you look at their suggested approach it's truly similar to what you have.

Instead of converting to a string, they are doing their checks a different way. If you wanted to stick with strings and get away from your warning, you could probably use the following:

var ipAddressAsString = httpContext.Connection.RemoteIpAddress?.ToString();

Where we have the ?. operator being used. Regardless, I think you're on the right track with your approach.

Pileate answered 22/5, 2023 at 23:44 Comment(2)
This confirms my approach but I am curious before I select this answer, what is the IsLoopBack() actually doing? Is it just confirming its not null or is a actual ip address?Eserine
It looks like it's just returning a static variable that's already been initialized. MSDN doesn't show much for it: learn.microsoft.com/en-us/dotnet/api/… But this github decompiled code looks like it must always be initialized: github.com/microsoft/referencesource/blob/… Hope that helps!Pileate

© 2022 - 2024 — McMap. All rights reserved.