HIGH cache poisoninghanamiapi keys

Cache Poisoning in Hanami with Api Keys

Cache Poisoning in Hanami with Api Keys — how this specific combination creates or exposes the vulnerability

Cache poisoning occurs when an attacker manipulates cached responses so that malicious or incorrect data is served to other users. In Hanami, using API keys as part of request handling can inadvertently contribute to this risk if cached responses are keyed improperly or cached without considering authentication context.

When API keys are used for authorization but the caching layer does not differentiate responses by key, a response generated for one key may be reused for another. For example, an endpoint that returns user-specific data might be cached based only on path and query parameters, ignoring the Authorization or custom X-API-Key header. An attacker who can observe or influence one user’s request might craft a request that causes a poisoned entry to be cached, and subsequent requests with different keys will receive the tainted response.

In Hanami, if you use a Rack-level cache such as Rack::Cache or a reverse proxy cache without including the API key in the cache key, you risk cross-key contamination. A real-world pattern is an endpoint like /api/v1/reports that accepts X-API-Key and returns data filtered by the associated account. If the cache key excludes the API key, User A’s data could be cached and then served to User B, leaking information or causing privilege issues. This is especially concerning when keys are rotated or reused across environments, as stale poisoned entries persist beyond their intended scope.

Another vector involves query parameters combined with headers. Suppose Hanami routes normalize URLs by stripping or reordering headers before caching. An attacker could send a request with a benign API key and a manipulated query (e.g., injecting a cache-control directive or a parameter that changes response headers). If the cache stores this response and serves it to requests with different API keys, the poisoned response may include sensitive headers or content not intended for those keys.

To map this to known attack patterns, consider this as a variant of the BOLA (Broken Level Authorization) and BFLA (Business Logic Flaws) categories, where trust boundaries are not properly enforced across cache layers. The OWASP API Security Top 10 lists Broken Object Level Authorization and Improper Asset Management; cache poisoning fits into these when cached data crosses authorization boundaries. In a supply-chain or SSRF context, if the API key is used to access internal resources, a poisoned cache could redirect or expose data across tenant boundaries.

Because middleBrick scans unauthenticated attack surfaces and tests input validation and property authorization, it can surface risks where cache differentiation by API key is missing. Findings will highlight inconsistencies between spec-defined authentication requirements and runtime behavior, helping you identify endpoints where keys are not properly considered in caching.

Api Keys-Specific Remediation in Hanami — concrete code fixes

To remediate cache poisoning when using API keys in Hanami, ensure cache keys incorporate the key or a normalized representation of authorization context. Avoid caching sensitive or key-specific responses unless you can guarantee isolation. Below are concrete patterns and code examples.

1. Include API key in cache key

When using a caching layer, derive the cache key from the API key and the request path. In Hanami, you can customize the cache middleware or your service objects to include the key.

# app/services/report_cache.rb
class ReportCache
  def initialize(cache_store = Hanami::Cache[:default])
    @cache = cache_store
  end

  def fetch(api_key, date_range)
    key = "report:#{api_key}:#{date_range}"
    @cache.fetch(key, expires_in: 300) do
      generate_report(api_key, date_range)
    end
  end

  private

  def generate_report(api_key, date_range)
    # business logic using api_key to scope data
  end
end

This ensures each API key gets its own cache entry, preventing cross-key contamination.

2. Avoid caching sensitive responses

For endpoints that return user-specific data, disable caching when an API key is present. You can set response headers to prevent intermediate caches from storing the response.

# app/actions/api/reports.rb
module Api
  class Reports < Hanami::Action
    def call(params)
      api_key = env['api.key']
      if api_key
        # Ensure no sensitive data is cached
        response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, private'
        response.headers['Pragma'] = 'no-cache'
        response.headers['Expires'] = '0'
      end
      # proceed with logic
    end
  end
end

3. Normalize and validate API keys before caching

Validate and normalize the API key to avoid cache key injection. Do not directly use raw keys in cache keys without sanitization to prevent header or query manipulation attacks.

# app/actions/api/base.rb
module Api
  class Base < Hanami::Action
    before do
      api_key = request.env['api.key']
      # Normalize: trim, enforce format, reject suspicious characters
      if api_key && api_key.match?(\A[a-zA-Z0-9\-_]{16}\z)
        @normalized_key = api_key.strip
      else
        halt 401, { error: 'invalid_key' }.to_json
      end
    end

    def cache_key(path)
      "v1:#{@normalized_key}:#{path}"
    end
  end
end

4. Use scope-aware caching for collections

If you must cache list endpoints, scope the cache to the API key and include strong validation of filters and pagination parameters to avoid poisoning through query manipulation.

# app/actions/api/items.rb
module Api
  class Items < Hanami::Action
    def call(params)
      api_key = env['api.key']
      scope = "items:user:#{api_key}:page:#{params[:page]}:per:#{params[:per]}"
      items = Hanami::Cache.fetch(scope, expires_in: 60) do
        repository(:items).for_user(api_key, params)
      end
      render 'items/index', items: items
    end
  end
end

5. Rotate keys and purge cache on rotation

When rotating API keys, proactively clear or version cached entries to eliminate poisoned data tied to old keys. This can be done with a background task that deletes keyspaced entries.

# lib/tasks/key_rotation.rb
namespace :cache do
  desc "Purge cache entries for a given API key prefix"
  task purge_key: :environment do
    key_prefix = ENV['KEY_PREFIX']
    Hanami::Cache.delete_matched("report:#{key_prefix}:*")
  end
end

By consistently including the API key in cache keys, disabling caching for sensitive responses, and validating key format, you reduce the risk of cache poisoning in Hanami applications. These practices align with remediation guidance that middleBrick provides in its findings, helping you address authorization and input validation issues flagged during scans.

Frequently Asked Questions

How can I verify that my Hanami API keys are not causing cache poisoning?
Use middleBrick to scan your endpoints; it tests input validation and property authorization and will flag missing key differentiation in cache behavior. Combine this with code reviews ensuring cache keys include the API key and sensitive responses set Cache-Control: no-store.
Does middleBrick test for cache poisoning specifically?
middleBrick’s checks for input validation, property authorization, and BOLA/IDOR help surface conditions that can lead to cache poisoning. It does not directly test cache behavior but highlights misconfigurations where keys are ignored in authorization boundaries, which you can then correlate with your caching implementation.