Cache Poisoning in Chi
How Cache Poisoning Manifests in Chi
Cache poisoning in Chi applications typically occurs when unvalidated user input is used to construct cache keys or when responses are cached without proper validation. Chi, as a lightweight HTTP router for Go, doesn't provide built-in caching mechanisms, but it's often used alongside middleware like github.com/go-chi/cache or integrated with reverse proxies and CDN layers.
The most common attack vector involves manipulating query parameters, headers, or path segments that influence caching decisions. For example, an endpoint like /api/users?id=123 might cache responses keyed on the id parameter. An attacker could exploit this by:
- Modifying query parameters to poison cache entries with malicious data
- Crafting requests that cause the application to cache error responses or sensitive information
- Exploiting inconsistent cache key generation across different request variations
Consider this vulnerable Chi middleware pattern:
func CacheMiddleware(next http.Handler) http.Handler {
cache := cache.New(5*time.Minute)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
key := r.URL.Path + r.URL.RawQuery
if cached, found := cache.Get(key); found {
w.Write(cached.([]byte))
return
}
// Capture response
rec := httptest.NewRecorder()
next.ServeHTTP(rec, r)
// Store response without validation
cache.Set(key, rec.Body.Bytes(), cache.DefaultExpiration)
w.Write(rec.Body.Bytes())
})
}This implementation is vulnerable because it caches responses based on raw user input without validating the content. An attacker could trigger error states or manipulate the response body before it's cached.
Chi-Specific Detection
Detecting cache poisoning in Chi applications requires examining both the application code and runtime behavior. Static analysis should focus on:
- Middleware that implements caching logic without proper input validation
- Usage of query parameters or headers in cache key construction
- Absence of cache key normalization or canonicalization
Dynamic detection with middleBrick can identify these issues by:
$ middlebrick scan https://api.example.com --profile=chi
[✓] Authentication bypass checks
[✓] BOLA/IDOR vulnerability scanning
[✓] Cache poisoning detection via parameter manipulation
[✓] Input validation analysis
Report generated: api-example.com-2024-03-15.json
Cache Poisoning Risk: MEDIUM
- Vulnerable cache key construction in /api/users
- Missing input validation on query parameters
- Potential for response manipulationmiddleBrick's Chi-specific scanning profile tests for common patterns including:
- Parameter-based cache key generation without validation
- Response caching of error states or sensitive data
- Inconsistent cache behavior across similar endpoints
The scanner actively manipulates request parameters to test for cache poisoning vulnerabilities, attempting to overwrite legitimate cache entries with malicious content or trigger inconsistent caching behavior.
Chi-Specific Remediation
Securing Chi applications against cache poisoning requires implementing proper validation and cache key management. Here's a secure implementation:
func SecureCacheMiddleware(next http.Handler) http.Handler {
cache := cache.New(5*time.Minute)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Validate and normalize cache key
key := normalizeCacheKey(r)
if cached, found := cache.Get(key); found {
w.Write(cached.([]byte))
return
}
// Validate response before caching
rec := httptest.NewRecorder()
next.ServeHTTP(rec, r)
// Only cache successful responses with safe content
if rec.Code == http.StatusOK && isSafeResponse(rec.Body.Bytes()) {
cache.Set(key, rec.Body.Bytes(), cache.DefaultExpiration)
}
w.WriteHeader(rec.Code)
w.Write(rec.Body.Bytes())
})
}
func normalizeCacheKey(r *http.Request) string {
// Remove dangerous characters, normalize case
path := strings.ToLower(r.URL.Path)
query := r.URL.RawQuery
// Only allow specific parameters
allowedParams := map[string]bool{"id": true, "page": true}
q, err := url.ParseQuery(query)
if err != nil {
return path
}
var safeParams []string
for k, v := range q {
if allowedParams[k] {
safeParams = append(safeParams, fmt.Sprintf("%s=%s", k, v[0]))
}
}
if len(safeParams) > 0 {
sort.Strings(safeParams)
return path + "?" + strings.Join(safeParams, "&")
}
return path
}
func isSafeResponse(body []byte) bool {
// Check for sensitive information, error messages, etc.
response := string(body)
return !strings.Contains(response, "password") &&
!strings.Contains(response, "error") &&
!strings.Contains(response, "stack trace")
}Additional security measures include:
- Implementing cache key canonicalization to prevent key collisions
- Setting appropriate cache TTLs based on content sensitivity
- Using cache tagging to invalidate related entries when data changes
- Implementing cache-aside patterns where the application validates cache hits
For production deployments, consider using established caching libraries with built-in security features rather than custom implementations. The github.com/go-chi/cache library provides configurable cache backends with proper validation hooks.