Api Key Enumeration in Aspnet
How API Key Enumeration Manifests in ASP.NET
API Key Enumeration in ASP.NET applications occurs when an attacker can systematically determine valid API keys by observing differences in application behavior between valid and invalid credentials. This is a specific instance of the OWASP API Top 10 category Broken Authentication (API2:2023). In ASP.NET, this vulnerability typically stems from custom authentication implementations that deviate from standard, secure patterns.
The most common manifestation is through inconsistent HTTP response status codes and body content. A poorly designed AuthenticationHandler or middleware might return a 200 OK with an application-specific error message like {"error":"Invalid API key"} for an invalid key, while a valid key returns 200 OK with the requested resource. This allows an attacker to script enumeration by observing the presence or absence of the expected success schema.
Another pattern is timing differences. If key validation involves a database lookup (e.g., against a ApiKeys table), an invalid key might short-circuit earlier than a valid key that triggers subsequent permission checks. While harder to detect remotely, this can be exploited with precise timing measurements.
A third ASP.NET-specific issue is error message leakage in development/debug modes. If the application is misconfigured to run in Development environment in production, unhandled exceptions from key validation (e.g., NullReferenceException when a key is null) can reveal stack traces or internal details that confirm a key's format is being processed.
Consider this vulnerable custom handler pattern:
public class VulnerableApiKeyHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue("X-API-Key", out var apiKeyValues))
{
return Task.FromResult(AuthenticateResult.Fail("Missing API Key"));
}
var providedKey = apiKeyValues.FirstOrDefault();
var isValid = _dbContext.ApiKeys.Any(k => k.Key == providedKey); // Simple check
if (!isValid)
{
// VULNERABILITY: Different response structure for invalid key
HttpContext.Response.StatusCode = 200; // Should be 401
return Task.FromResult(AuthenticateResult.Success(
new ClaimsPrincipal(new ClaimsIdentity("InvalidUser"))));
}
// ... successful authentication logic
}
}Here, an invalid key still results in a 200 status with a potentially distinguishable identity (InvalidUser), enabling enumeration.
ASP.NET-Specific Detection
Detecting API Key Enumeration requires analyzing how an endpoint responds to sequences of invalid, malformed, and valid-looking keys. A black-box scanner like middleBrick performs this by:
- Identifying API key injection points: It parses the OpenAPI/Swagger specification (if available) to find parameters/headers documented as authentication credentials (e.g.,
X-API-Key,Authorization: Bearer). For ASP.NET applications using[ApiController], common patterns include custom headers or query parameters. - Probing with crafted payloads: It sends a baseline request with no key, then with a sequence of known-invalid keys (e.g.,
"test","null","undefined",random GUIDs), and finally with a syntactically valid but unissued key format (e.g., a 32-character hex string if that's the expected pattern). - Analyzing response divergence: The scanner compares status codes, response body JSON schemas, and error message strings across the probe set. A vulnerability is flagged if there is a statistically significant difference between the response to an invalid key versus no key, or between different invalid keys. For example, if
no keyreturns401butinvalid key "test"returns200with{"code":1001}, enumeration is possible.
middleBrick's Authentication and BOLA/IDOR checks are particularly relevant. The scanner does not require credentials; it operates purely on the unauthenticated attack surface. For an ASP.NET Web API, you can run this detection from your terminal with the CLI tool:
middlebrick scan https://your-aspnet-api.com/api/values --checks Authentication,BOLAThis command initiates a 5–15 second scan focusing on authentication weaknesses, including key enumeration. The resulting report will show a per-category breakdown. If enumeration is found, it will appear under the Authentication category with a specific finding, such as "API Key validity can be inferred from response differences (Status Code 200 vs 401)", along with the affected endpoint and sample requests/responses.
The scanner also cross-references findings with the OpenAPI spec. If your ASP.NET app uses Swashbuckle/Swagger, middleBrick will resolve $ref definitions to understand the expected schema for error responses, making the divergence analysis more accurate.
ASP.NET-Specific Remediation
Remediation centers on ensuring that all responses to unauthenticated or unauthorized requests are consistent, generic, and use proper HTTP status codes. ASP.NET provides built-in mechanisms to achieve this without custom logic.
1. Use Standard Authentication Schemes with Consistent Challenges
Instead of a custom handler, leverage AddAuthentication with a scheme like ApiKey that derives from AuthenticationHandler<AuthenticationSchemeOptions>. Crucially, always return 401 Unauthorized for any missing or invalid credential, and never reveal whether the key itself was invalid versus missing. The framework's AuthenticateResult.Fail() method should be used, which by default triggers the WWW-Authenticate header challenge.
public class ConsistentApiKeyHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue("X-API-Key", out var apiKeyValues) ||
string.IsNullOrEmpty(apiKeyValues.FirstOrDefault()))
{
// Fail without distinction - same response as invalid key
return Task.FromResult(AuthenticateResult.Fail("Invalid credentials"));
}
var providedKey = apiKeyValues.FirstOrDefault();
var isValid = _dbContext.ApiKeys.Any(k => k.Key == providedKey);
if (!isValid)
{
// VULNERABILITY FIX: Fail with same message/code as missing key
return Task.FromResult(AuthenticateResult.Fail("Invalid credentials"));
}
var claims = new[] { new Claim(ClaimTypes.Name, "ApiUser") };
var identity = new ClaimsIdentity(claims, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(new ClaimsPrincipal(identity)));
}
}Register this in Program.cs:
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "ApiKey";
options.DefaultChallengeScheme = "ApiKey";
})
.AddScheme<AuthenticationSchemeOptions, ConsistentApiKeyHandler>("ApiKey", null);2. Global Exception and Status Code Handling
Use UseExceptionHandler and UseStatusCodePages to ensure that even unhandled exceptions or explicit 401/403 responses from authorization middleware ([Authorize]) do not leak information. Configure a generic problem details response for all 401 status codes.
app.UseExceptionHandler();
app.UseStatusCodePages(async context =>
{
if (context.HttpContext.Response.StatusCode == 401)
{
context.HttpContext.Response.ContentType = "application/json";
await context.HttpContext.Response.WriteAsJsonAsync(new
{
type = "https://httpstatuses.com/401",
title = "Unauthorized",
status = 401,
detail = "Authentication credentials were invalid or missing."
});
}
});3. Implement Rate Limiting on Authentication Endpoints
While not a direct fix for enumeration, rate limiting mitigates the attack's feasibility. Use the official AspNetCoreRateLimit package or .NET 7+ built-in rate limiting middleware. Apply it to all endpoints that process API keys, especially those without additional resource authorization.
// In Program.cs after AddRateLimiter
builder.Services.AddRateLimiter(options =>
{
options.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
RateLimitPartition.GetFixedWindowLimiter(
partitionKey: httpContext.User.Identity?.Name ?? httpContext.Request.Headers["X-API-Key"].ToString(),
factory: partition => new FixedWindowRateLimiterOptions
{
AutoReplenishment = true,
PermitLimit = 10, // 10 attempts per minute per key/IP
QueueLimit = 0,
Window = TimeSpan.FromMinutes(1)
}));
});
app.UseRateLimiter();4. Validate Key Format Early, Reject Uniformly
If your API keys have a known format (e.g., 32 hex characters), add a validation filter that rejects malformed keys before they hit the database, but still returns the same 401 response.
After applying these fixes, rescan with middleBrick. The Authentication category score should improve, and the specific enumeration finding should disappear. The remediation guidance provided by middleBrick will reference these ASP.NET-native patterns.