Cache Poisoning in Rails
How Cache Poisoning Manifests in Rails
Cache poisoning in Rails occurs when an attacker causes a cache entry to store attacker-controlled data that is later served to other users or to the same user in a different context. This typically exploits unsafe cache key construction or template rendering that includes request-derived values such as subdomain, locale, or unvalidated parameters. For example, using request.subdomain directly in a cache key can cause multiple users to share the same cached fragment if the subdomain is attacker-controlled.
Concrete attack patterns in Rails include:
- Fragment caching with keys that embed the current locale or subdomain without normalizing or isolating by tenant or user context. For instance,
cache(["v1", "profile", current_user.id, I18n.locale])is safer thancache(["profile", params[:id]])when the locale is derived from the request and can be manipulated. - Action or page caching where the cache key includes mutable path components like
params[:category]that an attacker can set to a value previously cached by another user, leading to unintended content disclosure. - Low-level cache stores (e.g.,
Rails.cache.fetch) used to memoize computed values that depend on request-scoped inputs without sanitization, enabling an attacker to poison entries that are later read by unrelated requests.
In Rails, common code paths that are vulnerable include controllers that pass unescaped parameters into cache key arrays, views that use instance variables derived from the query string, and background jobs that read from the cache using keys built from user input. Without proper isolation, a poisoned cache entry can be served across users, sessions, or even API responses, leading to information disclosure or incorrect application behavior.
Rails-Specific Detection
Detecting cache poisoning in Rails requires inspecting how cache keys are built and whether they incorporate mutable or attacker-influenced data. A safe cache key should exclude raw request parameters, should isolate data by tenant or user where applicable, and should avoid directly embedding values such as locale or subdomain unless they are constrained and validated.
Using middleBrick, you can submit your Rails application’s endpoints for a black-box scan that runs 12 security checks in parallel, including input validation and data exposure checks that surface unsafe caching behaviors. The scan analyzes the OpenAPI/Swagger spec (2.0, 3.0, 3.1) with full $ref resolution and cross-references definitions with runtime findings, helping identify endpoints where cache-related data exposure may occur. Reports include prioritized findings with severity and remediation guidance, enabling teams to focus on high-risk patterns such as unvalidated locale or subdomain usage in cache logic.
To manually review Rails code for cache poisoning risk, search for patterns such as:
# Unsafe: directly using params in cache key
cache(["items", params[:category]])
# Safer: isolating by current tenant and sanitizing input
cache(["items", current_tenant.id, params[:category].to_s.parameterize])
Look for uses of Rails.cache.fetch, fragment caching in views, and any inclusion of request-derived values in cache keys without tenant or user isolation. middleBrick’s findings can guide you toward these risky patterns by highlighting data exposure and input validation issues tied to caching routines.
Rails-Specific Remediation
Remediation focuses on ensuring cache keys are deterministic, tenant- or user-isolated, and free from attacker-controlled values. Use Rails’ built-in helpers to normalize inputs and scope cache entries to the appropriate context.
Key practices include:
- Avoid embedding raw
params,request.subdomain, orI18n.localedirectly in cache keys. Instead, map locale to a known set of values and validate subdomain presence and format. - Isolate cache entries by current tenant or user ID when applicable, and normalize string inputs using Rails’
parameterizeor a strict allowlist. - Prefer high-level helpers such as
cachein views, which integrates with Rails’ cache digests, over manual low-level cache keys where appropriate.
Examples of safe patterns in Rails:
# Unsafe pattern
cache(["v1", "search", params[:q], params[:filter]])
# Safe pattern: tenant-scoped, normalized inputs
cache(["v1", "search", current_tenant.id, params[:q].to_s.strip.gsub(/[^\w\s-]/, ''), params[:filter].to_sym])
# Unsafe: locale from request without validation
cache(["nav", I18n.locale, params[:page]])
# Safe: constrained locale and tenant isolation
valid_locale = I18n.available_locales.include?(params[:locale].to_sym) ? params[:locale].to_sym : I18n.default_locale
cache(["nav", current_tenant.id, valid_locale, params[:page]])
When using low-level caching, prefer namespaced keys and sanitize inputs explicitly. Rails’ cache store implementations do not mitigate poisoning; the application must ensure keys are safe. Regularly review cache key construction in controllers and views and leverage middleBrick’s continuous monitoring (available in the Pro plan) to detect regressions in production environments.