Cache Poisoning in Aspnet
Cache Poisoning in ASP.NET
Cache poisoning occurs when an attacker manipulates shared caching mechanisms to serve stale or malicious responses to legitimate users. In ASP.NET applications, this commonly happens when HTTP caching headers (such as Cache-Control, Expires, or Vary) are misconfigured, allowing sensitive or user-specific responses to be served from a shared cache like IIS Output Caching, Azure Front Door, or a reverse proxy.
Typical scenarios include:
- User-specific data (e.g., personalized dashboards) being cached without per-user variation in the
Varyheader. - Authentication tokens or session identifiers not being accounted for in cache keys.
- Improper use of
Donut Output Cachewhere fragments are cached inconsistently. - Reverse proxies caching
302 redirectresponses or error pages (e.g., 500 errors) that should be user-specific.
For example, consider an ASP.NET MVC action that returns user-specific data:
public ActionResult Dashboard()
{
var model = _userService.GetDashboardData(User.Identity.Name);
return View(model);
}
If the response is cached without regard to the authenticated user, an attacker could trigger a cache miss for one user and poison the cache with data intended for another. This is especially dangerous when applications use in-memory or distributed caches like MemoryCache or Redis without proper key scoping.
ASP.NET's output caching mechanism, when misconfigured, can inadvertently cache responses based on query strings or headers that do not fully distinguish users. For instance:
<system.web
href="http://schemas.microsoft.com/.NetConfiguration/v2/cacheSettings"
>
<outputCacheSettings policy="UseQueryString" />
<cacheAllowedVaryByParams="none" />
</system.web>
If policy="UseQueryString" is set but cacheAllowedVaryByParams="none", then different query strings do not create separate cache entries, leading to incorrect responses being served.
Real-world exploitation involves sending a request with a crafted query string or header that bypasses authentication checks while the response is cached for a different context. Attackers may use tools to iteratively poison caches by observing response differences and leveraging timing or error-based feedback.
Such vulnerabilities fall under the OWASP API Top 10 category Broken Object Level Authorization and can lead to data exposure, session hijacking, or privilege escalation when cached responses contain sensitive information intended for another user.
Detection in ASP.NET Applications
Detection of cache poisoning vulnerabilities in ASP.NET applications requires validating that caching mechanisms respect user context and authorization boundaries. Systems like middleBrick perform black-box scanning by analyzing HTTP responses for improper caching headers and cross-referencing them with request patterns.
For example, middleBrick might submit two nearly identical requests to an ASP.NET endpoint — one authenticated as User A with data {"user": "alice", "role": "admin"}, and another as User B with {"user": "bob", "role": "user"}. If the response to User B's request includes cached content that reflects User A's data (e.g., admin panel snippets), this indicates a potential cache poisoning vector.
middleBrick checks for the following indicators:
- Missing or overly broad
Varyheaders (e.g.,Vary: Accept-Encodingwithout user-specific variation). Cache-Controlheaders that permit public caching of authenticated responses.- Identical response bodies for requests with different authentication tokens or session cookies.
- Use of
DonutOutputCachewhere fragment caching does not properly isolate user roles.
During scanning, middleBrick sends parallel probe requests with manipulated query strings or headers to trigger different cache paths and observes whether responses vary appropriately. It also analyzes OpenAPI/Swagger specifications to detect endpoints marked as authenticated but observed to return unauthenticated content due to caching misconfigurations.
Findings include specific path examples, request headers used, and severity-based prioritization aligned with OWASP API Top 10. The platform provides remediation guidance directly tied to ASP.NET configuration best practices.
Remediation in ASP.NET
Remediation involves configuring ASP.NET's caching infrastructure to ensure that responses are varied by user context and authorization state. Key steps include:
- Set appropriate
Varyheaders to include authentication-related headers. For example:
<system.web
<caching>
<outputCacheLocation mode="Disabled" />
<outputCacheSettings policy="UseQueryString" />
<cacheAllowedVaryByParams="authUser" />
</caching>
</system.web>
- Use per-user caching in distributed caches. When using
MemoryCacheor Redis, include user identity in the cache key:
var cacheKey = $"dashboard:{User.Identity.Name}";
var cachedData = cache.Get(cacheKey);
if (cachedData == null)
{
cachedData = _userService.GetDashboardData(User.Identity.Name);
cache.Set(cacheKey, cachedData, TimeSpan.FromMinutes(10));
}
- Disable output caching for sensitive endpoints by adding a directive in
Web.configor via attributes:
<system.web
tag Elements="false" />
<pages validateRequest="true" />
- Apply role-based caching policies. For example, ensure admin panels are never cached:
[OutputCache(VaryByParam="none", Duration=0, Location=OutputCacheLocation.Any)]
[Authorize(Roles = "Admin")]
public ActionResult AdminPanel() { ... }
ASP.NET provides built-in support for fragment caching with DonutOutputCache, but developers must ensure that cached fragments are scoped to user roles. Using VaryByCustom="User"] and overriding GetVaryByCustom in Global.asax allows fine-grained control:
public override string GetVaryByCustom(HttpContext context)
{
if (context.Request.User.IsInRole("Admin"))
{
return "AdminDashboard";
}
return base.GetVaryByCustom(context);
}
These practices prevent unauthorized data from being served due to cache poisoning and align with secure caching patterns recommended by NIST and OWASP.