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 :
- verify if the host is unknown or localhost – then no limit.
- verify if header has Origin key
- 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 )
- if has – verify if it is the same with host
- if it is, then no limit
- if it is not, then simple limit
- 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 ” ————————–“