HIGH cache poisoningphoenixapi keys

Cache Poisoning in Phoenix with Api Keys

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

Cache poisoning occurs when an attacker manipulates cached responses so that subsequent users receive malicious or incorrect data. In Phoenix applications that rely on API keys for authorization, this risk arises when keys are treated as part of the cache key without careful consideration, or when responses are cached per-key without sufficient validation.

Phoenix typically uses caching mechanisms such as Cachex or Plug.Cache. If an endpoint varies the response based on an API key but the cache key includes the full key value, two issues emerge:

  • Unnecessary cache fragmentation: each distinct key creates a separate cache entry, reducing cache hit rates and increasing memory usage.
  • Opportunity for confusion: if a key is leaked or reused across environments, cached responses for one key might be served to another, exposing data or enabling cache-based side channels.

A concrete scenario: an unauthenticated or weakly authenticated route uses a query parameter like api_key to select a tenant or user-specific dataset. If the response is cached with a key that incorporates the API key value, an attacker who can predict or brute-force valid-looking keys may cause the server to cache responses that include sensitive data. Later, a different user with a different key might receive the poisoned cache if cache rules are misconfigured. Additionally, if the application caches upstream responses (e.g., from a third-party service) and embeds API keys in cache metadata without purging on key rotation, the poisoned data persists across requests.

Because middleBrick scans the unauthenticated attack surface, it can detect whether caching behavior differs significantly across API keys and highlight inconsistencies in how keys influence cache boundaries. This helps identify whether cache poisoning via key-dependent caching is present.

Api Keys-Specific Remediation in Phoenix — concrete code fixes

Remediation focuses on ensuring cache keys do not unnecessarily expose secrets and that responses are cached safely regardless of the API key value.

  • Do not include raw API keys in cache keys. Instead, use a stable identifier such as tenant ID or user ID that is mapped server-side to the key.
  • Normalize requests before caching: strip or ignore the API key header/parameter when determining cacheability, and enforce authorization after cache lookup.

Example: a Phoenix controller that authorizes via an API key in a header but caches a normalized response.

defmodule MyAppWeb.ItemController do
  use MyAppWeb, :controller

  # Simulated cache lookup/store interface
  # Replace with your actual cache backend (e.g., Cachex, Redis, Memcached)
  defp cache_get(key), do: nil
  defp cache_put(key, value, opts \\ []), do: :ok

  def show(conn, %{"id" => id}) do
    with {:ok, api_key} <- get_api_key(conn),
         {:ok, normalized_key} <- normalize_key(api_key),
         {:cache_hit, cached} <- get_cached(normalized_key, id) do
      send_resp(conn, 200, cached)
    else
      :error ->
        data = fetch_item_from_db(id)
        normalized_key = normalized_key_or_static(id)
        cache_put(normalized_key, data, ttl: 300)
        send_resp(conn, 200, data)
    end
  end

  defp get_api_key(conn) do
    case conn.req_headers["x-api-key"] do
      nil -> {:error, :missing_key}
      key -> {:ok, key}
    end
  end

  defp normalize_key(api_key) do
    # Map the API key to a non-sensitive tenant/user identifier
    # This mapping should be validated server-side and never expose the raw key in cache keys
    case MyApp.Auth.lookup_by_key(api_key) do
      {:ok, %{tenant_id: tid}} -> {:ok, {:tenant, tid}}
      _ -> :error
    end
  end

  defp get_cached({:tenant, tid}, id) do
    key = {tid, id}
    case cache_get(key) do
      nil -> {:cache_miss, nil}
      val -> {:cache_hit, val}
    end
  end

  defp cache_put({:tenant, tid}, data, opts) do
    key = {tid, id}
    cache_put(key, data, opts)
  end

  defp normalized_key_or_static(id) do
    # Fallback static key when normalization fails; avoids key-dependent cache entries
    {"public", id}
  end
end

In the above, the raw API key is never part of the cache key. Instead, a server-side mapping produces a stable, non-sensitive identifier. Additionally, ensure that any caching layer or CDN does not automatically use request headers (including API keys) to vary cache without explicit rules. Regularly rotate keys and purge related cache entries to limit the window of exposure.

middleBrick can validate that your endpoints do not leak keys into cache metadata and that responses remain consistent across authorized requests, supporting your remediation workflow.

Frequently Asked Questions

Can cache poisoning via API keys expose data across tenants in a multi-tenant Phoenix app?
Yes. If cache keys incorporate raw API keys and keys are predictable or improperly isolated, cached responses from one tenant can be served to another, leading to cross-tenant data exposure.
Does middleBrick test for cache poisoning in Phoenix applications using API keys?
middleBrick scans the unauthenticated attack surface and can identify inconsistent caching behaviors related to API key handling, highlighting potential cache poisoning risks and providing remediation guidance.