Rate Limiting Bypass in Aspnet with Basic Auth
Rate Limiting Bypass in Aspnet with Basic Auth — how this specific combination creates or exposes the vulnerability
In ASP.NET applications, combining Basic Authentication with weak or absent rate limiting can enable Rate Limiting Bypass, where an attacker avoids throttling controls by manipulating authentication and request patterns. Basic Authentication sends credentials in an Authorization header as base64-encoded username:password. Because base64 is reversible but not encrypted, credentials are easily decoded if transmitted without TLS. When rate limiting is applied only at the IP or endpoint level without considering authenticated identity, an attacker can rotate credentials or use multiple accounts to evade limits.
A common bypass scenario occurs when rate limiting is scoped to IP address only and Basic Auth is accepted without validating credential validity on each request. For example, if an endpoint allows unlimited requests per IP but the Basic Auth credentials are accepted with a hardcoded or default set of users, an attacker can repeatedly change the Authorization header with valid credentials from different accounts. Because the identity of the user is not factored into the rate limit bucket, the attacker effectively multiplies request capacity by the number of valid accounts they possess or by using credential lists from prior breaches.
Another bypass pattern involves partial enforcement where rate limiting is applied after authentication middleware but before the API logic, yet the authentication middleware itself does not enforce per-user limits. In ASP.NET, the order of middleware matters. If UseAuthentication and UseAuthorization run before the rate-limiting middleware, an attacker can send a high volume of authenticated requests that bypass a global limit applied earlier in the pipeline. Even with Basic Auth, if the server validates credentials on each request but does not bind rate limits to the authenticated user identity (e.g., via claims or a principal), the same IP can authenticate with many users to exhaust resources.
Additionally, Basic Auth does not inherently carry scope or role metadata that rate limit policies might leverage. Without enriching the rate limit key with user identity or roles, policies cannot differentiate between low-privilege and high-privilege accounts. This becomes critical when default or guest accounts with Basic Auth remain active. An attacker authenticating as a low-privilege user can still saturate backend services if rate limiting does not differentiate by authenticated user, leading to denial of service for others or enabling enumeration attacks through timing differences.
ASP.NET Core offers built-in mechanisms to bind rate limiting to authentication state. For example, using policy-based rate limiting that incorporates user identity enables more granular controls. Developers should ensure rate limiting considers both IP and authenticated user identifiers, and should avoid accepting Basic Auth over non-TLS channels. MiddleBrick’s scans detect whether authentication is combined with per-user or role-based rate limiting, highlighting cases where identity-aware throttling is absent and increasing the risk of bypass.
Basic Auth-Specific Remediation in Aspnet — concrete code fixes
To mitigate Rate Limiting Bypass with Basic Auth in ASP.NET, enforce identity-aware rate limiting and always protect credentials with TLS. Use policy-based rate limiting that incorporates user identity, and validate credentials on each request while avoiding default or shared accounts.
Enforce TLS
Never accept Basic Auth over non-encrypted channels. Configure Kestrel to require HTTPS and redirect HTTP to HTTPS.
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(5001, listenOptions =>
{
listenOptions.UseHttps();
});
});
var app = builder.Build();
app.UseHttpsRedirection();
app.Run();
Identity-aware rate limiting
Use ASP.NET Core’s built-in rate limiting with policy-based approaches that consider authenticated user identity. Combine user identity with IP to reduce bypass risk.
// Program.cs
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(_ =>
RateLimitPartition.GetSlidingWindowLimiter(
partitionKeySelector: context =>
RateLimitPartitionKey.GetKey(
context.User.Identity?.Name ?? context.Connection.RemoteIpAddress?.ToString() ?? "unknown"),
factoryConfig: _ => new SlidingWindowRateLimiterOptions
{
PermitLimit = 100,
Window = TimeSpan.FromMinutes(1),
SegmentsPerWindow = 4,
QueueProcessingOrder = QueueProcessingOrder.OldestFirst,
AutoReplenishment = true
}));
});
app.UseRateLimiter();
Secure Basic Auth validation
Validate credentials on each request and avoid accepting default accounts. Use IAuthenticationHandler or custom middleware to parse and verify the Authorization header securely.
// BasicAuthMiddleware.cs
public class BasicAuthMiddleware
{
private readonly RequestDelegate _next;
public BasicAuthMiddleware(RequestDelegate next) => _next = next;
public async Task InvokeAsync(HttpContext context)
{
if (!context.Request.Headers.TryGetValue("Authorization", out var authHeader))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Missing Authorization header");
return;
}
if (!authHeader.ToString().StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
{
context.Response.StatusCode = 400;
await context.Response.WriteAsync("Invalid Authorization header");
return;
}
var token = authHeader.ToString()?.Substring("Basic ".Length)?.Trim();
var credentialString = Encoding.UTF8.GetString(Convert.FromBase64String(token ?? string.Empty));
var credentials = credentialString.Split(':', 2);
var username = credentials[0];
var password = credentials[1];
// Validate credentials securely (example with constant-time comparison)
if (!IsValidUser(username, password))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Invalid credentials");
return;
}
var claims = new[] { new Claim(ClaimTypes.Name, username) };
var identity = new ClaimsIdentity(claims, "Basic");
context.User = new ClaimsPrincipal(identity);
await _next(context);
}
private static bool IsValidUser(string username, string password)
{
// Replace with secure store lookup and constant-time verification
return string.Equals(username, "alice", StringComparison.Ordinal) &&
string.Equals(password, "s3cur3P@ss!", StringComparison.Ordinal);
}
}
// Extension to register middleware
public static class BasicAuthMiddlewareExtensions
{
public static IApplicationBuilder UseBasicAuth(this IApplicationBuilder builder) =>
builder.UseMiddleware<BasicAuthMiddleware>();
}
// In Program.cs
app.UseBasicAuth();
Additional hardening
- Rotate credentials regularly and avoid shared or default accounts.
- Apply per-user rate limits in addition to global limits.
- Monitor authentication failures and implement progressive delays or lockouts for repeated invalid attempts.
- Combine with other middleware such as CORS and anti-forgery where appropriate to reduce abuse surface.
These changes ensure that rate limiting is bound to authenticated identity and that Basic Auth is only used over encrypted channels, reducing the risk of Rate Limiting Bypass in ASP.NET applications.
Related CWEs: resourceConsumption
| CWE ID | Name | Severity |
|---|---|---|
| CWE-400 | Uncontrolled Resource Consumption | HIGH |
| CWE-770 | Allocation of Resources Without Limits | MEDIUM |
| CWE-799 | Improper Control of Interaction Frequency | MEDIUM |
| CWE-835 | Infinite Loop | HIGH |
| CWE-1050 | Excessive Platform Resource Consumption | MEDIUM |