HIGH rate limiting bypassaspnetmutual tls

Rate Limiting Bypass in Aspnet with Mutual Tls

Rate Limiting Bypass in Aspnet with Mutual Tls

Rate limiting in ASP.NET is typically enforced based on identifiers such as IP address, API key, or client certificate thumbprint. When mutual TLS (mTLS) is used for authentication, the server validates the client certificate during the TLS handshake and may treat the request as authenticated before the ASP.NET application layer applies rate limits. This ordering can create a bypass if rate limiting is configured only at the application level and relies on identifiers that are not bound to the validated certificate.

In an ASP.NET Core application, developers often use middleware such as UseRateLimiter with a partition key derived from context.Connection.RemoteIpAddress or a custom header. When mTLS is enforced at the reverse proxy or load balancer (e.g., in front of Kestrel), the proxy may terminate TLS and forward requests to the application over HTTP. If the proxy does not forward the client certificate or maps it to a generic identifier, the ASP.NET app may see requests from a single IP or a default certificate identity. This homogeneity can allow an attacker to exhaust the quota allocated to that identifier, effectively bypassing intended per-client limits.

The risk is compounded when the application also uses endpoint authorization policies that depend on certificate claims but do not enforce rate limits at the same granularity. For example, a policy may validate the certificate’s subject or enhanced key usage to permit access, while rate limiting is applied only to unauthenticated paths. An authenticated client with a valid certificate could then target rate-limited endpoints without being subjected to stricter throttling, especially if the implementation does not bind rate limit partitions to certificate metadata such as serial number or public key fingerprint.

middleBrick detects this category of issue through unauthenticated scanning and cross-references runtime behavior with OpenAPI specifications. For mTLS-enabled APIs, it checks whether rate limiting mechanisms are evaluated in a way that accounts for client certificate identity and whether partitioning aligns with certificate-bound attributes. Findings include recommendations to align rate limiting partitions with mTLS identities and to enforce limits as close as possible to the transport layer, ensuring that throttling reflects the authenticated client rather than a shared proxy address.

Mutual Tls-Specific Remediation in Aspnet

To remediate rate limiting bypass risks in ASP.NET with mutual TLS, bind rate limit partitions to certificate properties that cannot be spoofed and ensure that throttling logic is applied before or alongside authorization checks. Use the certificate’s public key fingerprint or serial number as a stable partition key, and avoid relying solely on IP addresses when mTLS is in use.

Below are concrete code examples for configuring mTLS and a certificate-aware rate limiter in ASP.NET Core.

Enabling Mutual TLS in ASP.NET Core

Configure Kestrel to require client certificates and map certificate properties into claims for use in authorization and rate limiting.

// Program.cs
using System.Security.Cryptography.X509Certificates;
var builder = WebApplication.CreateBuilder(args);

// Require client certificates
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureHttpsDefaults(httpsOptions =>
    {
        httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
        httpsOptions.AllowedCipherSuitesPolicy = new CipherSuitesPolicy(new[]
        {
            System.Security.Authentication.CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
            System.Security.Authentication.CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        });
    });
});

// Add certificate-based authentication schemes (for policy use)
builder.Services.AddAuthentication("Certificate").AddCertificate(options =>
{
    options.AllowedCertificateTypes = CertificateTypes.All;
    options.RevocationMode = X509RevocationMode.NoCheck;
    options.Events = new CertificateAuthenticationEvents
    {
        OnCertificateValidated = context =>
        {
            var cert = context.ClientCertificate;
            if (cert == null)
            {
                context.Fail("No client certificate");
                return Task.CompletedTask;
            }
            var claims = new[]
            {
                new Claim(ClaimTypes.NameIdentifier, cert.Thumbprint),
                new Claim("CertSerial", cert.SerialNumber),
                new Claim("CertFingerprint", cert.GetCertHashString())
            };
            var identity = new ClaimsIdentity(claims, context.Scheme.Name);
            context.Principal = new ClaimsPrincipal(identity);
            return Task.CompletedTask;
        }
    };
});

builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", () => "Hello mTLS");
app.Run();

Certificate-Aware Rate Limiting

Use a partition key that incorporates certificate metadata so that each validated client is uniquely throttled.

// Rate limiting policy with certificate partition key
var fixedWindowOptions = new FixedWindowRateLimiterOptions
{
    PermitLimit = 100,
    Window = TimeSpan.FromMinutes(1),
    PartitioningStrategy = PartitioningStrategy.Header,
    // Custom header is set by middleware that reads the certificate fingerprint
    // Alternatively, use a custom partition resolver
};

builder.Services.AddRateLimiter(options =>
{
    options.AddPolicy("mTlsPolicy", ctx =>
    {
        // Extract certificate fingerprint from claims added in OnCertificateValidated
        var httpContext = (HttpContext)ctx;
        var certFingerprint = httpContext.User.FindFirst("CertFingerprint")?.Value;
        var partitionKey = certFingerprint ?? httpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
        return RateLimitPartition.GetSlidingWindowLimiter(
            partitionKey,
            _ => new SlidingWindowRateLimiterOptions
            {
                PermitLimit = 100,
                Window = TimeSpan.FromMinutes(1),
                SegmentsPerWindow = 4
            });
    });
});

app.UseRateLimiter();
app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/secure", (HttpContext context) =>
    {
        return Results.Ok($"Request allowed for {context.User.Identity?.Name}");
    }).RequireAuthorization("Certificate");
});

Ensure that any reverse proxy or load balancer forwards or preserves the client certificate information if it terminates TLS. Rate limiting logic should favor certificate-bound identifiers over potentially malleable headers to prevent bypass via IP sharing or header manipulation.

Related CWEs: resourceConsumption

CWE IDNameSeverity
CWE-400Uncontrolled Resource Consumption HIGH
CWE-770Allocation of Resources Without Limits MEDIUM
CWE-799Improper Control of Interaction Frequency MEDIUM
CWE-835Infinite Loop HIGH
CWE-1050Excessive Platform Resource Consumption MEDIUM

Frequently Asked Questions

Can a proxy that terminates mTLS weaken rate limiting?
Yes. If the proxy terminates mutual TLS and forwards requests without preserving client certificate metadata, the ASP.NET app may lose per-client identity and apply rate limits based on a shared IP or default identity, enabling bypass.
Should rate limiting be applied before or after authentication in mTLS scenarios?
Apply rate limiting before or concurrently with authentication, using certificate-bound keys, so that authenticated clients are still subject to per-identity throttling and cannot exhaust quotas meant for others.