Rate Limiter–CORS limited

The problem is that , for the site ( Angular , React, plain HTML ) deployed into wwwroot of .NET Core, I want to have unlimited requests. Also, if I made requests to localhost ( when I try from local), I want also unlimited requests. However, if other site make requests to my AP( CORS enabled ) I, should have limited requests .

There is the Stefan Prodan package – https://github.com/stefanprodan/AspNetCoreRateLimit  . And in .NET Core 7 has appeared https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit  with 4 limiters  -please be sure that you read the article https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit .

Back to the problem:

So we have 2 limits; NoLimit if same site and a simple limiter( 3 request per minute )_ if coming via other site.

var noLimit = RateLimitPartition.GetNoLimiter(“”);

Func<string, RateLimitPartition<string>> simpleLimiter =
     (string address) =>
RateLimitPartition.GetFixedWindowLimiter(address, _ =>
{
     return new FixedWindowRateLimiterOptions()
     {
         PermitLimit = 3,
         Window = TimeSpan.FromMinutes(1),
         QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
         QueueLimit = 1
     };

});

Now, let’s see how we apply :

  1. verify if the host is unknown or  localhost – then no limit.
  2. verify if header has Origin key
    1. if has not – could be same site or desktop – then no limit ( if the xhr request to same site, Edge sends “Origin” header – Chrome does not! – same as desktop )
    2. if has – verify if it is the same with host
      1. if it is, then no limit
      2. if it is not, then simple limit
  3. return default simple limit

And this is the code

builder.Services.AddRateLimiter(opt =>
{
     opt.RejectionStatusCode = StatusCodes.Status429TooManyRequests;
     opt.OnRejected = (ctx, ct) =>
     {
         ctx.HttpContext.Response.Headers.Add(“tiltLimit”, “please try later”);
         return ValueTask.CompletedTask;
     };
     opt.AddPolicy(“UnlimitMeAndLocalHost”, context =>
     {
        
         var host = context.Request.Host;
         var hostName = host.HasValue ? host.Host : “”;
         if (string.IsNullOrWhiteSpace(hostName))
         {
             Console.WriteLine(“no host???”);
             return simpleLimiter(“”);
         }
         if (string.Equals(hostName,”localhost”,StringComparison.InvariantCultureIgnoreCase))
         {
             Console.WriteLine(“localhost have no limit”);
             return noLimit;
         }
         //chrome does not send origin, if same site
         var hasOrigin = context.Request.Headers.TryGetValue(“Origin”, out var origin);
         //maybe also verify referer?
         if (!hasOrigin)
         {
             Console.WriteLine(“no origin -same site?”);
             return noLimit;

        }
         //edge sends origin
         var originHost = origin.ToString();
         //removing scheme
         if (originHost.StartsWith(“http://”))
         {
             originHost = originHost.Substring(7);
         }
         if (originHost.StartsWith(“https://”))
         {
             originHost = originHost.Substring(8);
         }
         var fullHost = context.Request.Host.Host;
         Console.WriteLine($”has origin {originHost} , full host {fullHost}”);
         if (string.Equals(fullHost,originHost, StringComparison.CurrentCultureIgnoreCase))
         {
             Console.WriteLine($”same site – no cors”);
             return noLimit;
         }
         //return noLimit;
         return simpleLimiter(origin.ToString());
     });

Pretty complicated – read again the algorithm above.

As a proof of concept , browse to https://ignatandrei.github.io/TILT/tilt/public  and to http://tiltwebapp.azurewebsites.net/AngTilt/ with developer tools open –  you will see 429 errors for the first, but not for the second.

And for seeing in action , execute 4 times fast this powershell

cls
$x=””
$hostName = “https://tiltwebapp.azurewebsites.net”
#$hostName = “http://localhost:9900″

$fullUrl = $hostName + “/api/PublicTILTs/LatestTILTs/ignatandrei/1”

$session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
$session.UserAgent = “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.62”
$x = Invoke-WebRequest -UseBasicParsing -Uri $fullUrl `
-WebSession $session `
-Headers @{
  “Origin” = “http://www.foo.com/”
“Accept-Encoding”=”gzip, deflate, br”
   “Accept-Language”=”en-US,en;q=0.9”
   “Referer”=”https://netcoreusefullendpoints.azurewebsites.net/swagger/index.html”
   “accept”=”application/json”
   “sec-ch-ua”=”`”Microsoft Edge`”;v=`”107`”, `”Chromium`”;v=`”107`”, `”Not=A?Brand`”;v=`”24`””
   “sec-ch-ua-mobile”=”?0″
   “sec-ch-ua-platform”=”`”Windows`””
}

$x.StatusCode
Write-Host ” ————————–“