Credential Stuffing in Aspnet
How Credential Stuffing Manifests in Aspnet
Credential stuffing attacks against ASP.NET applications typically target authentication endpoints where attackers automate login attempts using credential pairs leaked from other breaches. In ASP.NET Core, this often manifests at the /Account/Login endpoint or custom API controllers handling [HttpPost] login actions. Attackers exploit the absence of rate limiting or bot mitigation on these endpoints, sending high-volume POST requests with JSON payloads like { "email": "attacker@breached.com", "password": "LeakedPassword123" }.
Specific ASP.NET code patterns that increase vulnerability include:
- Using
[AllowAnonymous]on login actions without complementary throttling. - Relying solely on
SignInManager.PasswordSignInAsyncwithout checking for failed attempt patterns. - Custom middleware that fails to increment failure counters per IP or username after
SignInResult.Failed. - Omitting
AddPasswordlessTokenProviderorAddDefaultTokenProvidersinIdentityOptions, weakening token-based fallback protections. - Logging successful/failed attempts without correlation IDs, hindering attack detection in logs.
Real-world examples include CVE-2020-0609 (though not direct stuffing, it highlights auth bypass risks in ASP.NET) and observed attacks on ASP.NET Core 3.1+ applications where UseAuthentication() was configured without UseRateLimiting() or UseResponseCaching() for anomaly detection.
Aspnet-Specific Detection
Detecting credential stuffing in ASP.NET requires observing behavioral patterns rather than static code flaws. middleBrick identifies this risk by scanning the unauthenticated attack surface and testing for missing mitigations on login endpoints. During its 5–15 second black-box scan, it:
- Sends sequential login attempts with varied credential pairs to
/Account/Loginor equivalent API routes. - Measures response times and status codes (e.g., 200 OK vs 401 Unauthorized) to infer absence of delay mechanisms.
- Checks for missing security headers like
Retry-AfterorX-RateLimit-Remainingthat would indicate rate limiting. - Validates whether the endpoint returns consistent error messages (avoiding user enumeration) but lacks timing-based throttling.
- Cross-references OpenAPI specs (if present) to confirm
AllowAnonymousattributes on POST login actions without429 Too Many Requestsresponses in examples.
For example, if middleBrick scans an ASP.NET Core Web API and finds that POST /api/auth/login returns 401 Unauthorized instantly for 10 consecutive failed attempts—without ever returning 429 or introducing delays—it flags missing rate limiting as a high-risk finding under the "Rate Limiting" category, directly enabling credential stuffing.
Developers can simulate this locally using:
// In Program.cs - missing rate limiting (vulnerable)
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
// No AddRateLimiter() or UseRateLimiting()
var app = builder.Build();
app.MapPost("/api/auth/login", (LoginModel model) =>
{
// SignInManager logic here - no failure tracking
return Results.Unauthorized();
}).AllowAnonymous();
app.Run();
middleBrick would detect this as exploitable because the endpoint accepts anonymous POSTs with no observable throttling.
Aspnet-Specific Remediation
Mitigating credential stuffing in ASP.NET requires implementing layered defenses using native framework features. Key remediations include:
- Rate Limiting: Use
Microsoft.AspNetCore.RateLimitingpackage to enforce limits per IP or username. - Response Caching for Anomaly Detection: Combine with
AddResponseCaching()to serve cached error responses under load. - Identity Configuration: Enable lockout via
IdentityOptions.Lockoutafter failed attempts. - Custom Middleware: Track failures per identifier (email/username) and introduce progressive delays.
- CAPTCHA Integration: For high-risk scenarios, use
Google.ReCaptcha.V3after threshold breaches (note: middleBrick does not fix—this is guidance).
Example: Implementing rate limiting and identity lockout in Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Add rate limiting service
builder.Services.AddRateLimiter(_ => _
.AddFixedWindowLimiter(policyName: "login", options =>
{
options.PermitLimit = 5; // 5 attempts
options.Window = TimeSpan.FromMinutes(15);
options.QueueLimit = 0;
}));
// Configure Identity with lockout
builder.Services.AddIdentity(options =>
{
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
})
.AddEntityFrameworkStores();
var app = builder.Build();
app.UseRateLimiter(); // Enforce limits
app.UseAuthentication();
app.MapPost("/api/auth/login", async (LoginModel model, SignInManager signInManager) =>
{
var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, false, lockoutOnFailure: true);
if (result.IsLockedOut)
return Results.StatusCode(423); // Locked
if (!result.Succeeded)
return Results.Unauthorized();
return Results.Ok();
})
.RequireRateLimiting("login") // Apply named policy
.AllowAnonymous();
app.Run();
This configuration ensures:
- After 5 failed login attempts from an IP within 15 minutes, further requests are blocked (rate limiting).
- After 5 failed attempts for a specific username, the account is locked for 30 minutes (Identity lockout).
- middleBrick would detect these mitigations by observing
429or423responses during scan, reducing the risk score.
Note: middleBrick does not apply these fixes—it identifies the missing protections and provides this remediation guidance.