24

I'm building an ASP.NET MVC site where I want to limit how often authenticated users can use some functions of the site.

Although I understand how rate-limiting works fundamentally, I can't visualize how to implement it programatically without creating a major code smell.

Can you point me towards a simple yet powerful solution for approaching such a problem, with C# sample code?

If it matters, all of these functions are currently expressed as Actions that only accept HTTP POST. I may eventually want to implement rate-limiting for HTTP GET functions as well, so I'm looking for a solution that works for all such circumstances.

2

3 Answers 3

40

If you are using IIS 7 you could take a look at the Dynamic IP Restrictions Extension. Another possibility is to implement this as an action filter:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class RateLimitAttribute : ActionFilterAttribute
{
    public int Seconds { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Using the IP Address here as part of the key but you could modify
        // and use the username if you are going to limit only authenticated users
        // filterContext.HttpContext.User.Identity.Name
        var key = string.Format("{0}-{1}-{2}",
            filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
            filterContext.ActionDescriptor.ActionName,
            filterContext.HttpContext.Request.UserHostAddress
        );
        var allowExecute = false;

        if (HttpRuntime.Cache[key] == null)
        {
            HttpRuntime.Cache.Add(key,
                true,
                null,
                DateTime.Now.AddSeconds(Seconds),
                Cache.NoSlidingExpiration,
                CacheItemPriority.Low,
                null);
            allowExecute = true;
        }

        if (!allowExecute)
        {
            filterContext.Result = new ContentResult
            {
                Content = string.Format("You can call this every {0} seconds", Seconds)
            };
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
        }
    }
}

And then decorate the action that needs to be limited:

[RateLimit(Seconds = 10)]
public ActionResult Index()
{
    return View();
}
Sign up to request clarification or add additional context in comments.

6 Comments

How would you extend this to use 429 - Too Many Requests as per RFC 6586
@StuartBlacker I would assume you would edit the following line: filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict; from Conflict to a 429 error.
Great solution here. I love it. I'm going to see if I can do sth (something) similar. Maybe sth with an LRU or sliding window timeout.
filterContext.Result = new HttpStatusCodeResult(429, "too many requests");
We tried this and encountered problems with users having the same IP address, such as multiple users connecting from the same large organization (Ford, for example.) There are other attributes of the request that we could use, but all of the ones I have seen so far are "spoofable" (user-agent, SessionID, request headers, etc.)
|
5

Have a look at Jarrod's answer on how they do this on SO.

StackOverflow MVC Throttling

Some example code as well as explanation on how it works.

2 Comments

I have a doubt , What if does DOS attack hits the web server, then how come this code level protection will help ? Doesn't we need solution on web server or machine level ?
@stom - If the DOS attack is making to your webservers, you have already lost a decent amount of the battle. Even if the web server simply drops the packets, the attack is till tying up bandwidth and server resources to listen, inspect and drop the packet(s). Rate Limiting is more to prevent abuse from a single user or bots, not necessarily a total defense against DOS attacks though it may help a bit in that you're not executing DB calls/functions for every request like you would with no rate throttling in place.
3

.Net 6

Check it out:

Nuget: https://www.nuget.org/packages/DotNetRateLimiter/

It is simple:

[HttpGet("")]
[RateLimit(PeriodInSec = 60, Limit = 3)]
public IEnumerable<WeatherForecast> Get()
{
    ....
}

And even you can control request by route or query parameters:

[HttpGet("by-query/{id}")]
[RateLimit(PeriodInSec = 60, Limit = 3, RouteParams = "id", QueryParams = "name,family")]
public IEnumerable<WeatherForecast> Get(int id, string name, [FromQuery] List<string> family)
{
    ....
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.