Cache Poisoning in Gorilla Mux with Basic Auth
Cache Poisoning in Gorilla Mux with Basic Auth — how this specific combination creates or exposes the vulnerability
Cache poisoning occurs when an attacker causes a shared cache to store malicious content, leading other users to receive harmful responses. When using Gorilla Mux with HTTP Basic Authentication, the risk arises because the cache key may not sufficiently differentiate between authenticated and unauthenticated contexts or between different users. If a cached response is generated from a request that includes Basic Auth credentials and that response is later served to a different user without credentials, sensitive information can be leaked or an authenticated session can be hijacked.
Gorilla Mux is a powerful URL router and dispatcher, but it does not inherently manage cache behavior. Developers often integrate it with reverse proxies or in-memory caches. The combination of Basic Auth and caching becomes dangerous when the Authorization header is either omitted from the cache key or improperly handled. For example, if a request with Authorization: Basic dXNlcjpwYXNz is cached and then served to another user who did not provide credentials, the cached response may expose data that should be restricted. Similarly, if user-specific data is included in a response that is cached based only on the request path, an attacker who can cause a victim’s browser to make an authenticated request might poison the cache with content tailored to the victim.
Real-world attack patterns involve inducing authenticated users to request a resource that includes sensitive data, which is then cached and subsequently retrieved by unauthenticated users. This can violate confidentiality and lead to information disclosure. In the context of OWASP API Top 10, this maps closely to Broken Object Level Authorization (BOLA) and Cache Poisoning categories. MiddleBrick scans detect such misconfigurations by analyzing OpenAPI specifications and runtime behavior, identifying cases where Authorization headers are not considered in cache partitioning or where responses containing sensitive data lack proper cache-control directives.
Common misconfigurations include failing to set Vary: Authorization on responses that vary by credentials and caching authenticated responses in shared storage. Without this header, intermediaries may incorrectly treat responses as interchangeable. Additionally, if cache keys are constructed solely from the request URI and query parameters, they neglect the Authorization header, enabling cross-user contamination. MiddleBrick’s checks for Data Exposure and Authentication validate these aspects, ensuring that cached responses respect user boundaries and that proper cache-control headers are present.
Basic Auth-Specific Remediation in Gorilla Mux — concrete code fixes
To mitigate cache poisoning when using Basic Auth with Gorilla Mux, ensure that authenticated responses are not cached in shared storage and that cache keys incorporate the Authorization header. Use explicit cache-control headers and the Vary header to instruct caches on how to differentiate responses.
Example: Secure handler with cache-control and vary headers
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func authenticatedHandler(w http.ResponseWriter, r *http.Request) {
// Validate Basic Auth credentials
username, password, ok := r.BasicAuth()
if !ok || !isValidUser(username, password) {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Set cache-control to prevent shared caching of authenticated responses
w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, private")
// Vary on Authorization to ensure caches store separate versions per user
w.Header().Set("Vary", "Authorization")
// Safe, user-specific response
fmt.Fprintf(w, "Hello, %s", username)
}
func isValidUser(username, password string) bool {
// Implement secure credential validation
return username == "alice" && password == "secret"
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/user/profile", authenticatedHandler).Methods("GET")
http.ListenAndServe(":8080", r)
}
Example: Middleware to enforce no-cache for authenticated routes
func noCacheMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// If request includes Basic Auth, prevent caching
if _, _, ok := r.BasicAuth(); ok {
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Vary", "Authorization")
}
next.ServeHTTP(w, r)
})
}
func main() {
r := mux.NewRouter()
// Apply middleware globally or to specific routes
r.Use(noCacheMiddleware)
r.HandleFunc("/public", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Cache-Control", "public, max-age=3600")
w.Write([]byte("public data"))
}).Methods("GET")
http.ListenAndServe(":8080", r)
}
Operational guidance
- Always set
Cache-Control: no-storeorprivatefor responses containing user-specific data after Basic Auth validation. - Include
Vary: Authorizationon any response that changes based on credentials to prevent shared caches from serving one user’s response to another. - Avoid caching authenticated responses in shared caches; prefer private caches or non-cached flows for sensitive endpoints.
- Audit OpenAPI specs and runtime behavior using tools like MiddleBrick to detect missing cache headers or improper authentication handling.