Cache Poisoning in Gin with Basic Auth
Cache Poisoning in Gin with Basic Auth — how this specific combination creates or exposes the vulnerability
Cache poisoning in the Gin framework when Basic Authentication is used occurs when an attacker can influence cached responses through manipulation of request headers or parameters. Because Basic Auth credentials are typically sent in the Authorization header, variations in that header can cause upstream caches to treat authenticated and unauthenticated or differently authenticated requests as distinct cache entries. If caching logic does not properly exclude the Authorization header or normalize the cache key, an attacker may inject a poisoned response that other users receive, leading to unauthorized data exposure or incorrect content being served.
In Gin, developers may inadvertently configure caching at the application or infrastructure layer without stripping or normalizing the Authorization header. For example, if a caching layer uses the full request path and headers as the cache key, two requests to the same endpoint—one with a valid Authorization header and one without—might produce separate cache entries. An attacker with network proximity could observe or manipulate requests to place a malicious response in the cache under a predictable key, which is then served to other users. This is especially risky when responses include user-specific data that should not be shared across users.
Additionally, if query parameters are used to carry session or token-like values that are not properly validated, they can contribute to cache key divergence and enable an attacker to poison the cache through parameter manipulation. Gin applications that rely on middleware for authentication must ensure that sensitive headers are excluded from cache keys and that responses with authentication-dependent content are either not cached or cached with strict normalization. Without these precautions, an attacker can leverage the combination of Basic Auth and caching misconfiguration to serve malicious or incorrect content to other users.
Basic Auth-Specific Remediation in Gin — concrete code fixes
To mitigate cache poisoning risks in Gin with Basic Auth, you should normalize cache keys by removing or omitting the Authorization header from cache consideration and avoid caching sensitive responses. Below are concrete code examples demonstrating secure handling of Basic Auth in Gin and guidance on cache behavior.
- Example: Basic Auth middleware that validates credentials without caching authenticated responses
// main.go
package main
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
// BasicAuth is a simple Basic Auth middleware that checks credentials.
// It sets a context flag to indicate an authenticated request.
func BasicAuth() gin.HandlerFunc {
return func(c *gin.Context) {
auth := c.GetHeader("Authorization")
if auth == "" || !strings.HasPrefix(auth, "Basic ") {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "authorization header required"})
return
}
// In production, decode and validate against a secure store.
// This example performs a simple check for demonstration.
token := auth[len("Basic "):]
if token != "dXNlcjpwYXNz" { // "user:pass" base64-encoded
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "invalid credentials"})
return
}
c.Set("authenticated", true)
c.Next()
}
}
// publicHandler is safe to cache because it does not require authentication.
func publicHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "public data"})
}
// privateHandler requires authentication and must not be cached.
func privateHandler(c *gin.Context) {
// Mark response as private and prevent caching by upstream components.
c.Header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0")
c.Header("Pragma", "no-cache")
c.JSON(http.StatusOK, gin.H{"message": "private data for authenticated user"})
}
func main() {
r := gin.Default()
// Public endpoint: can be cached by infrastructure if needed.
r.GET("/public", publicHandler)
// Protected endpoint: authenticated only and must not be cached.
r.GET("/private", BasicAuth(), privateHandler)
r.Run()
}
- Ensure caching layers exclude the Authorization header
When using a reverse proxy or CDN, configure it to not use the Authorization header as part of the cache key. This prevents different authenticated requests from creating multiple cache entries for the same resource. In Gin, you can also ensure responses that contain user-specific data set appropriate cache-control headers to prevent caching entirely.
- Validate and normalize inputs that affect caching
Avoid using raw query parameters or headers to drive cache keys without normalization. If your application must vary responses based on roles or tenant, include a normalized identifier in the cache key rather than raw credentials. Combine this with short cache lifetimes for sensitive endpoints and validate that cached responses do not leak private data across users.