DEV Community

Mohammed Ahmed Hussien
Mohammed Ahmed Hussien

Posted on

Rate Limiting in ASP.NET MVC 5

One of the most important parts in any application is to secure your application against Denial of Services attacks (DoS) (to learn more about this attack consult Wikipedia for more details https://en.wikipedia.org/wiki/Denial-of-service_attack), and ASP.NET MVC application is not exception of that attack, in ASP.NET Core there is a built-in Middleware that can help you to do that, but in ASP.NET MVC that target .NET Framework you have to implement this take by yourself, and in this post, I will show you how!
Create a new ASP.NET MVC 5 Application that target .NET Framework (Here I'm going to use a Web API 2.2 application, but it doesn't matter which application you're going to use because the sample that we're going to implement is applicable for both type of application)
I'm going to create a new Action Filter named CustomRateLimitAttribute and this attribute should be inherited from ActionFilterAttribute
In the next section I will put the complete code after that I will explain it in details:

public class CustomRateLimitAttribute : ActionFilterAttribute
{
    private int _limit = 10; // Allowed Request Count.
    private TimeSpan _window = TimeSpan.FromMinutes(1); // Time Window
    private static MemoryCache _cache = MemoryCache.Default;
    private object _lock = new object();
    public CustomRateLimitAttribute(int limit, int window)
    {
        _limit = limit;
        _window = TimeSpan.FromMinutes(window);
    }

    public override bool AllowMultiple
    {
        get { return false; }
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var ip = HttpContext.Current.Request.UserHostAddress;
        var key = $"{ip}_{actionContext.ActionDescriptor.ControllerDescriptor.ControllerName}_{actionContext.ActionDescriptor.ActionName}";

        lock (_lock)
        {
            var count = _cache.Get(key) as int? ?? 0;
            if (count >= _limit)
            {
                actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Conflict);
                actionContext.Response.Content = new System.Net.Http.StringContent("Too many request and Rate limit exceeded.");
                return;
            }
            _cache.Set(key, count + 1, DateTimeOffset.UtcNow.Add(_window));
        }
        base.OnActionExecuting(actionContext);
    }
Enter fullscreen mode Exit fullscreen mode

So, first I define multiple private fields, the first one is _limit and the purpose of this filed is to define how many requests are allowed in a specific time and the default time is stored in the next private field _window and by default is one minute, but I give a user to override the default value by pass it in the constructor of the attribute.
Second, I defined a filed named __cache _of type MemoryCache and this field will help to store the number of the requests and compare it to the default limit requests that is defined in the _limit field and to track the number of the request I created unique key which contins of the Controller name, the Action name and the IP address of the user that trying to attack the application.

MemoryCache object is defined in System.Runtime.Caching namespace

Now you're ready to use this attribute, in the HomeController class you can decorate the entire controller by this new attribute, or you can apply it to a specific Action method, in the next example I decorate an action method named GetFiles() to accept a limit number of requests (5 requests duration one minute per user):

   // api/home/getfiles
   [HttpGet]
   [Route("api/home/getfiles")]
   [CustomRateLimit(limit: 5, window: 1)]
   public HttpResponseMessage GetFiles()
   {

   }
Enter fullscreen mode Exit fullscreen mode

And that's all.

Top comments (0)