HIGH cache poisoningchibasic auth

Cache Poisoning in Chi with Basic Auth

Cache Poisoning in Chi 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 unintentionally. In Chi, a lightweight HTTP router for Go, this risk can emerge when routes are cached based on request attributes that include authorization headers such as Basic Auth. Because Basic Auth credentials are typically sent in the Authorization header on every request, developers sometimes configure caching rules to vary cache keys by that header to isolate user-specific content. However, if this approach is applied without strict normalization and validation, it can inadvertently allow an attacker to poison the cache for a shared key space or cause sensitive user credentials to be cached and inadvertently exposed.

Consider a Chi API that caches responses per user to improve performance. If the cache key includes the raw Authorization header value, an attacker could supply a specially crafted header that results in a cacheable response being stored under a key that other users might later receive. For example, an endpoint that returns user profile data might be cached with a key derived from the Authorization header. If the attacker manipulates the header, they could cause the cache to store a response containing sensitive data or malicious content under a key that maps to another user’s route. Subsequent requests with a different, valid Basic Auth credential might then retrieve the poisoned entry, leading to information disclosure or incorrect behavior.

Chi itself does not implement caching; caching is typically implemented via middleware or external systems like Redis or in-memory stores. The framework passes context and request details to middleware, where developers define cache keys. If Basic Auth credentials are used directly in cache key construction without hashing or stripping them, the attack surface expands. An unauthenticated attacker does not need to break authentication; they simply need to understand or guess how cache keys are derived. The vulnerability is not in Chi’s routing logic, but in how the surrounding middleware combines routing, caching, and authentication metadata. Because scans from middleBrick include checks for BOLA/IDOR and BFLA/Privilege Escalation, such misconfigurations in cache-based authorization handling can be surfaced as actionable findings with remediation guidance.

Basic Auth-Specific Remediation in Chi — concrete code fixes

To mitigate cache poisoning when using Basic Auth in Chi, avoid using raw credentials in cache keys. Instead, derive a stable, non-sensitive identifier such as a user ID or username after validating and decoding the credentials. Ensure that cache keys are deterministic and scoped appropriately, and do not allow attacker-controlled values to influence cached responses.

Example: Safe Basic Auth parsing and cache key derivation in Chi

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using System.Security.Claims;
using System.Text.Encoding;
using System.Text;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication("Basic")
    .AddScheme("Basic", null);
builder.Services.AddAuthorization();
builder.Services.AddHttpClient();
builder.Services.AddMemoryCache();
var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/profile", (HttpContext context, IMemoryCache cache) =>
{
    var user = context.User;
    if (user.Identity is not { IsAuthenticated: true })
    {
        return Results.Unauthorized();
    }

    // Derive a safe cache key from a stable claim, never from raw credentials
    var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value
                 ?? user.FindFirst(ClaimTypes.Name)?.Value;
    if (userId == null)
    {
        return Results.Forbid();
    }

    const string cacheKeyPrefix = "user_profile_";
    if (!cache.TryGetValue(cacheKeyPrefix + userId, out Profile profile))
    {
        // Simulate fetching profile data
        profile = new Profile { UserId = userId, DisplayName = $"User_{userId}" };
        cache.Set(cacheKeyPrefix + userId, profile, TimeSpan.FromMinutes(5));
    }

    return Results.Ok(profile);
});

app.Run();

public class Profile
{
    public string UserId { get; set; } = string.Empty;
    public string DisplayName { get; set; } = string.Empty;
}

public class BasicHandler : AuthenticationHandler
{
    public BasicHandler(
        IOptionsMonitor options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock) : base(options, logger, encoder, clock) { }

    protected override async Task HandleAuthenticateAsync()
    {
        if (!Request.Headers.ContainsKey("Authorization"))
        {
            return AuthenticateResult.NoResult();
        }

        var authHeader = Request.Headers["Authorization"].ToString() ?? string.Empty;
        if (!authHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
        {
            return AuthenticateResult.NoResult();
        }

        var token = authHeader.Substring("Basic ".Length).Trim();
        var credentialBytes = Convert.FromBase64String(token);
        var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':', 2);
        if (credentials.Length != 2)
        {
            return AuthenticateResult.NoResult();
        }

        var (username, password) = (credentials[0], credentials[1]);
        // Replace with real validation logic against your user store
        if (username == "alice" && password == "secret")
        {
            var claims = new[]
            {
                new Claim(ClaimTypes.NameIdentifier, "user-123"),
                new Claim(ClaimTypes.Name, username)
            };
            var identity = new ClaimsIdentity(claims, Scheme.Name);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, Scheme.Name);
            return AuthenticateResult.Success(ticket);
        }

        return AuthenticateResult.Fail("Invalid credentials.");
    }
}

In this example, the cache key uses a stable user identifier (NameIdentifier claim) instead of the raw Authorization header. This prevents attackers from influencing the cache key through credential manipulation. MiddleBrick scans can help detect related misconfigurations by analyzing API specifications and runtime behavior for improper authorization handling in caching contexts.

Frequently Asked Questions

Can cache poisoning occur even if Basic Auth credentials are validated successfully?
Yes. Cache poisoning can occur if the cache key includes attacker-influenced values, such as raw headers, even when credentials are valid. Always derive cache keys from stable, server-side identifiers and avoid including secrets or raw user input in cache keys.
Does middleBrick detect cache poisoning risks related to Basic Auth?
middleBrick scans for BOLA/IDOR and BFLA/Privilege Escalation patterns and maps findings to frameworks like OWASP API Top 10. While it does not test caching internals directly, misconfigurations in how authorization data influences cache behavior can appear in findings with remediation guidance.