Cache Poisoning in Aspnet with Basic Auth
Cache Poisoning in Aspnet with Basic Auth — how this specific combination creates or exposes the vulnerability
Cache poisoning occurs when an attacker causes a cache to store malicious content, leading other users to receive that content. In ASP.NET applications using HTTP caching mechanisms (e.g., response caches or upstream proxies/CDNs), combining weak authentication such as Basic Auth with cache-sensitive endpoints can expose cached data across users and sessions.
Basic Auth transmits credentials in an Authorization header encoded as Base64, without inherent encryption unless HTTPS is enforced. If an ASP.NET endpoint includes user-specific or role-specific data in the response and lacks proper Vary headers, a shared cache may incorrectly reuse a response cached for one user to another. For example, an endpoint like /api/users/profile that caches based on URL alone may return one user’s data if the cache key does not account for the Authorization header or user identity. An authenticated request from User A could be cached; if User B makes a subsequent request without proper cache differentiation, User B might receive User A’s cached response, which may contain sensitive information.
The risk is compounded when caching is applied before authentication or authorization logic, or when responses are cached with insufficient Vary directives. Consider an ASP.NET Core app using response caching middleware without excluding the Authorization header from the cache key. A proxy or in-memory cache might treat requests with different Authorization headers as identical if the cache key is based solely on the request path. This can lead to privilege escalation where a user sees another user’s data, or exposure of session-related content if tokens or sensitive headers are cached.
In the context of middleBrick’s LLM/AI Security checks, unauthenticated LLM endpoints in ASP.NET APIs can be probed for improper exposure of system prompts or outputs, while input validation and property authorization checks help identify endpoints where cache keys omit authorization context. Properly configuring cache policies and Vary headers mitigates cache poisoning by ensuring responses are segregated per user or per authorization context.
Basic Auth-Specific Remediation in Aspnet — concrete code fixes
To mitigate cache poisoning with Basic Auth in ASP.NET, ensure that authenticated responses are cached with user-specific keys and that sensitive endpoints disable caching or enforce strict Vary rules. Below are concrete code examples for ASP.NET Core that demonstrate secure handling of Basic Auth and cache control.
Secure Basic Auth with per-user cache policy
Configure authentication and ensure responses are varied by user identity. Use AddAuthentication and policy-based authorization, then apply cache directives that incorporate user identifiers.
// Program.cs or Startup.cs
using Microsoft.AspNetCore.Authentication.Basic;
var builder = WebApplication.CreateBuilder(args);
// Configure Basic Authentication
builder.Services.AddAuthentication(BasicAuthenticationDefaults.AuthenticationScheme)
.AddBasic(options =>
{
options.Events = new BasicAuthenticationEvents
{
OnValidatePrincipal = context =>
{
// Validate username/password from context.User.Identity.Name / context.Password
var username = context.User.Identity.Name;
var password = context.Password;
if (IsValidUser(username, password))
{
var identity = new ClaimsIdentity(context.Scheme.Name);
identity.AddClaim(new Claim(ClaimTypes.Name, username));
context.User = new ClaimsPrincipal(identity);
context.Success();
}
else
{
context.Fail("Invalid credentials");
}
return Task.CompletedTask;
}
};
});
builder.Services.AddAuthorization();
builder.Services.AddResponseCaching();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.UseResponseCaching();
app.MapGet("/api/users/profile", (ClaimsPrincipal user) =>
{
// Return user-specific data
return Results.Json(new { Profile = $"Data for {user.Identity.Name}" });
})
.RequireAuthorization()
.AddOutputCache(opt =>
{
opt.VaryByRules = new Dictionary<string, string>
{
{ "AuthorizationHeader", "req.headers[\"Authorization\"]" }
};
// Alternatively, vary by user name to ensure per-user caching
opt.VaryByCustom = "User.Identity.Name";
});
app.Run();
bool IsValidUser(string username, string password)
{
// Implement secure validation (e.g., constant-time comparison, hashed credentials)
return !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password);
}
Disable caching for sensitive authenticated endpoints
For endpoints that return sensitive user-specific data, explicitly disable caching to prevent storage of responses in shared caches.
app.MapGet("/api/users/me", (ClaimsPrincipal user) =>
{
// Sensitive data — do not cache
return Results.Ok(new { User = user.Identity.Name, Role = "User" });
})
.RequireAuthorization()
.AddOutputCache(opt =>
{
opt.NoStore();
opt.VaryByRules = new Dictionary<string, string>
{
{ "Authorization", "req.headers[\"Authorization\"]" }
};
});
Cache-Control and Vary headers for proxy-level caching
Ensure responses include explicit Cache-Control and Vary headers to instruct caches to differentiate by Authorization header or user identity.
app.MapGet("/api/data", (ClaimsPrincipal user) =>
{
var response = Results.Ok(new { Message = "Public but user-specific context" });
response.Headers.CacheControl = "private, max-age=60";
response.Headers.Vary = "Authorization";
return response;
})
.RequireAuthorization();
These practices reduce the likelihood of cache poisoning by ensuring cached responses are scoped appropriately and that Basic Auth credentials do not inadvertently expose data across users.
Frequently Asked Questions
How does middleBrick detect cache poisoning risks in ASP.NET APIs?
Can the middleBrick CLI scan endpoints using Basic Auth safely?
middlebrick scan <url> against endpoints using Basic Auth. It tests the unauthenticated attack surface and includes checks such as Authentication and Data Exposure to surface cache-related findings without requiring credentials.