Cache Poisoning in Chi with Cockroachdb
Cache Poisoning in Chi with Cockroachdb — how this specific combination creates or exposes the vulnerability
Cache poisoning occurs when an attacker manipulates cached responses so that subsequent requests receive malicious or incorrect data. In a Chi-based service that uses Cockroachdb as the authoritative data store, the risk arises when responses are cached based on request parameters that include user-controlled or untrusted input. If input validation and parameter normalization are incomplete, an attacker can craft requests that produce responses cached under distinct keys, causing other users or downstream services to receive poisoned data.
Chi is a small, composable router for Go, and it does not provide built-in caching; caching is typically implemented via middleware or an external cache layer. When integrating with Cockroachdb, developers may cache query results keyed by URL path, query parameters, or tenant identifiers. If the cache key does not incorporate tenant context or proper input canonicalization, an attacker can inject crafted query parameters that cause the application to cache a response under a key that other users will later retrieve. For example, a user-specific query like /accounts?user_id=123 might be cached with a key that does not fully isolate tenant or account ID, enabling an attacker to submit /accounts?user_id=123&role=admin and have the poisoned response served to users who do not have admin privileges.
Cockroachdb’s SQL interface and transaction semantics can inadvertently contribute to cache poisoning when application logic caches rows or result sets without validating that the cached representation matches the current authorization context. If a cached SELECT query uses placeholders that are not fully bound to validated tenant or scope attributes, an attacker may leverage SQL injection or schema manipulation to alter what is stored in the cache. Additionally, because Cockroachdb supports distributed transactions and serializable isolation, long-lived cached result sets might not reflect the latest write conflicts or permission changes, leading to stale and potentially malicious data being served.
To illustrate, consider a Chi route that queries Cockroachdb for a user profile and caches the JSON response. If the cache key is derived only from the numeric user ID provided in the URL without verifying ownership or tenant boundaries, an attacker can request another user’s profile and cause the application to cache that response under a generic key. Subsequent requests for that key will receive the attacker’s data, leading to information disclosure or privilege escalation. This pattern aligns with BOLA/IDOR findings that middleBrick detects when scanning Chi endpoints that rely on Cockroachdb without proper object-level authorization checks.
middleBrick scans such endpoints to identify missing parameter validation, weak cache-key design, and improper authorization checks that enable cache poisoning. The scanner’s checks include input validation, property authorization, and BOLA/IDOR testing, which can surface these risks in unauthenticated scans. Findings include severity-ranked guidance on ensuring cache keys incorporate tenant context, validating and normalizing all inputs, and avoiding caching of sensitive responses without strict authorization checks.
Cockroachdb-Specific Remediation in Chi — concrete code fixes
Remediation focuses on strict input validation, canonical cache-key construction, and ensuring that cached data respects tenant and ownership boundaries. In Chi, use middleware to normalize request parameters and embed tenant identifiers into cache keys. When querying Cockroachdb, always use prepared statements with bound parameters and avoid interpolating user input into SQL strings.
First, define a cache key that includes the tenant ID and validated user ID. For example:
// cacheKey builds a deterministic, tenant-aware cache key.
func cacheKey(tenantID, userID string) string {
return fmt.Sprintf("tenant:%s:userprofile:%s", tenantID, userID)
}
Second, validate and bind inputs before using them in SQL queries. In Chi, extract parameters via chi.URLParam and validate them before passing to Cockroachdb:
// userHandler retrieves a user profile with tenant-aware caching and safe SQL.
func userHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
tenantID := r.Context().Value("tenantID").(string)
userID := chi.URLParam(r, "userID")
if !isValidUUID(userID) || tenantID == "" {
http.Error(w, "invalid request", http.StatusBadRequest)
return
}
ctx := context.Background()
var profile UserProfile
key := cacheKey(tenantID, userID)
if found := cache.Get(ctx, key, &profile); !found {
row := db.QueryRowContext(ctx, "SELECT id, name, email FROM users WHERE id = $1 AND tenant_id = $2", userID, tenantID)
if err := row.Scan(&profile.ID, &profile.Name, &profile.Email); err != nil {
http.Error(w, "not found", http.StatusNotFound)
return
}
cache.Set(ctx, key, profile, time.Hour)
}
JSON(w, http.StatusOK, profile)
}
}
Third, avoid caching responses that contain sensitive or user-specific data unless the cache key is guaranteed to be unique per authorized context. For queries that must remain dynamic, disable caching or set very short TTLs. When using Cockroachdb, prefer parameterized queries over string concatenation to prevent injection that could alter cache contents:
// safeQuery uses parameterized statements to prevent injection.
func safeQuery(db *sql.DB, tenantID, userID string) (*UserProfile, error) {
ctx := context.Background()
profile := &UserProfile{}
err := db.QueryRowContext(ctx,
"SELECT id, name, email FROM users WHERE id = $1 AND tenant_id = $2",
userID, tenantID).Scan(&profile.ID, &profile.Name, &profile.Email)
return profile, err
}
Finally, implement cache invalidation strategies that account for writes to Cockroachdb. When a user updates their profile, evict or update the corresponding cache entry using the same tenant-aware key. This reduces the window where poisoned data can be served. middleBrick’s findings around input validation, property authorization, and BOLA/IDOR help prioritize these fixes by highlighting where cache boundaries and authorization checks are missing.