Cache Poisoning in Chi (Go)
Cache Poisoning in Chi with Go — how this specific combination creates or exposes the vulnerability
Cache poisoning occurs when an attacker tricks a cache (or cache-like behavior) into storing a malicious response, causing subsequent users to receive attacker-controlled data. In a Go HTTP router like Chi, this can arise when route parameters or query values are used to construct cache keys without proper normalization and validation. If a handler reflects user input directly into the response and that response is cached based on the raw request, an attacker can inject crafted inputs that alter cached entries for other users.
Chi does not provide built-in caching; developers typically add caching at the handler or middleware layer. When using Chi’s context.URLParam or context.QueryParam to vary cache keys, failing to validate, sanitize, or scope these values can lead to cache poisoning. For example, a handler that uses a user-supplied Accept-Language header or a path parameter to decide response content might inadvertently cache a poisoned response. Attackers can exploit this to serve malicious content, bypass language or regional restrictions, or manipulate business logic that depends on cache state.
Real-world patterns that increase risk include dynamic route definitions where parameters influence backend selection or response formatting, and handlers that embed parameters in cache keys without canonicalization. If the application uses shared caches (e.g., in-memory caches keyed by request attributes), a poisoned key can overwrite legitimate entries. Because Chi routes are matched in order, a malicious route parameter that matches a broader pattern can intercept requests and alter cached outcomes. Such issues are not inherent to Chi but stem from how developers design caching strategies and handle input within Chi handlers.
Go-Specific Remediation in Chi — concrete code fixes
To mitigate cache poisoning in Chi, treat all user-controlled data as untrusted when building cache keys. Validate and sanitize inputs, avoid using raw parameters or headers directly in cache logic, and scope cache entries with explicit context such as authenticated user identifiers. Below are concrete Go examples demonstrating secure patterns.
Validate and canonicalize route parameters
Use strict parameter validation and canonicalization before using values in cache keys. For a numeric ID, ensure it is parsed and bounded:
import (
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
)
func safeHandler(rw http.ResponseWriter, r *http.Request) {
idStr := chi.URLParam(r, "id")
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
http.Error(r, "invalid id", http.StatusBadRequest)
return
}
// Use the validated integer ID for cache key construction
cacheKey := "resource:v1:id:" + strconv.Itoa(id)
// proceed with cache lookup/handling
http.Fprintln(rw, "safe response")
}
Avoid using raw headers in cache keys
Do not directly incorporate headers like Accept-Language into cache keys without normalization and allowlisting:
import (
"strings"
"github.com/go-chi/chi/v5"
)
func languageAwareHandler(rw http.ResponseWriter, r *http.Request) {
lang := chi.URLParam(r, "lang")
// Canonicalize: restrict to supported languages
supported := map[string]bool{"en": true, "fr": true, "es": true}
if !supported[lang] {
lang = "en" // default
}
cacheKey := "page:lang=" + lang
// use cacheKey safely
http.Fprintln(rw, "response for "+lang)
}
Scope cache entries with request context
Include user or tenant identifiers when available to avoid cross-user contamination:
import (
"github.com/go-chi/chi/v5"
)
func scopedCacheMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
userID := "anonymous"
if authenticatedID := r.Header.Get("X-User-ID"); authenticatedID != "" {
userID = authenticatedID
}
// Example: store userID in context for downstream cache logic
ctx := context.WithValue(r.Context(), "cacheContext", userID)
next.ServeHTTP(rw, r.WithContext(ctx))
})
}
Use middleware to sanitize inputs globally
Register Chi middleware to validate or strip unexpected query parameters that could poison cache keys:
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
)
func main() {
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
// Remove or normalize query parameters that should not affect cache behavior
r.URL.RawQuery = "safe=true" // example: enforce safe query state
next.ServeHTTP(rw, r)
})
})
r.Get("/items/{id}", safeHandler)
}