HIGH api key exposurephoenixopenid connect

Api Key Exposure in Phoenix with Openid Connect

Api Key Exposure in Phoenix with Openid Connect — how this specific combination creates or exposes the vulnerability

When a Phoenix application exposes API keys while using OpenID Connect (OIDC) for authentication, the combination can unintentionally disclose secrets through logs, client-side code, or misconfigured endpoints. In Phoenix, API keys are often stored in environment variables and injected into the runtime configuration. If those keys are referenced in client-side templates, embedded in JavaScript assets, or logged at the framework or library level, they become readable to an attacker who can inspect browser sources or server logs.

OpenID Connect introduces flows that exchange authorization codes for tokens. If the client secret or a bearer token tied to an API key is included in front-channel requests, query parameters, or URLs, network observers or browser history can capture them. For example, using the implicit flow historically placed tokens in the URL fragment, which is both insecure and visible in Referer headers. Even with modern authorization code flow, failing to bind tokens tightly to the backend client can cause the API key or client secret to be mishandled by custom token-introspection or relay logic.

In a black-box scan, middleBrick tests whether sensitive keys appear in responses, are leaked through error messages, or are derivable from misconfigured endpoints. A Phoenix endpoint that echoes configuration or exposes debug pages might return the API key in plaintext or in a recoverable form. Similarly, OIDC discovery documents that include incorrect redirect URIs or permissive CORS settings can widen the exposure surface by allowing unauthorized origins to participate in the flow and observe tokens or keys.

Consider a scenario where an API key is used to call a third-party service from within Phoenix, and the key is stored in System.get_env("EXTERNAL_API_KEY"). If a controller action logs the full configuration map for debugging, the key can appear in structured logs and be harvested by log aggregation tools. If the OIDC client also attaches that key as an extra header for downstream calls, any captured log line reveals both authentication context and the raw key, enabling replay or lateral movement.

middleBrick’s LLM/AI Security checks are particularly effective at detecting when API keys or client secrets could be coaxed out through prompt injection or output leakage. For instance, a prompt designed to extract system instructions might inadvertently trigger an error path that returns stack traces containing OIDC configuration, including redirect URIs and client identifiers, alongside any hardcoded or poorly namespaced API keys.

To detect this in practice, a scan can submit an unauthenticated request to an endpoint that initiates OIDC authorization, then follow the redirect chain while inspecting response headers and body content. If the API key appears in query strings, fragments, or JSON responses, middleBrick flags it as data exposure. The scanner also checks whether introspection or token-exchange endpoints return keys or secrets in plain text, and whether metadata endpoints disclose configuration that should remain internal.

Openid Connect-Specific Remediation in Phoenix — concrete code fixes

Remediation centers on ensuring API keys and OIDC credentials never travel in browser-visible contexts and are strictly confined to backend processes. Use the authorization code flow with Proof Key for Code Exchange (PKCE) and avoid any flow that returns tokens in URLs. In Phoenix, store API keys and OIDC client secrets exclusively in server-side environment variables or encrypted secrets, and reference them only in controlled code paths.

Secure OIDC Configuration Example

Configure the OIDC provider with strict redirect URIs, limit scopes to the minimum required, and enforce HTTPS. Below is a realistic server-side setup using ueberauth and ueberauth_google where the client secret is never exposed to the client:

config :ueberauth, Ueberauth,
  providers: [
    google: {Ueberauth.Strategy.Google, [default_scope: "openid email profile"]}
  ]

config :ueberauth, Ueberauth.Strategy.Google.OAuth,
  client_id: System.get_env("GOOGLE_CLIENT_ID"),
  client_secret: System.get_env("GOOGLE_CLIENT_SECRET")

In your controller, avoid passing tokens or secrets to the view layer. Instead, keep token exchange and API calls server-side:

defmodule MyAppWeb.AuthController do
  use MyAppWeb, :controller

  def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do
    # Exchange code for tokens happens inside ueberauth; tokens are not exposed to JS
    api_key = System.get_env("EXTERNAL_API_KEY")

    # Use the key server-side only
    case ExternalApi.fetch_data(auth.credentials.token, api_key) do
      {:ok, data} -> json(conn, %{data: data})
      {:error, reason} -> send_resp(conn, 500, "Internal error")
    end
  end
end

Ensure that any API key used for outbound calls is not logged. Wrap external calls with log filtering to prevent accidental exposure:

defmodule MyApp.ExternalApi do
  @api_key System.get_env("EXTERNAL_API_KEY")

  def fetch_data(token, api_key) do
    # Avoid logging the key; use structured logging with sensitive fields redacted
    url = "https://api.example.com/v1/resource"
    headers = [
      {"Authorization", "Bearer #{token}"},
      {"X-API-Key", api_key}
    ]

    # Perform request without printing key
    HTTPoison.get!(url, headers, hackney: [recv_timeout: 5000])
    |> Map.put(:headers, redact_headers(&1.headers))
  end

  defp redact_headers(headers) do
    Enum.map(headers, fn
      {"X-API-Key", _} -> {"X-API-Key", "[REDACTED]"}
      {k, v} -> {k, v}
    end)
  end
end

For OIDC-specific hardening, validate nonce and issuer on the server and do not rely on client-supplied values. Use plug-based session management to store minimal user information and avoid embedding credentials in the session cookie. middleBrick’s checks for BOLA/IDOR and Property Authorization help verify that tokens and keys are validated per-request and not assumed from URL parameters.

In CI/CD, the GitHub Action can enforce that no commit introduces environment variables with names like API_KEY or CLIENT_SECRET in plaintext within diffs. The MCP Server allows you to scan from your IDE and catch accidental exposure before code reaches staging.

Frequently Asked Questions

Can an API key exposed in Phoenix logs lead to compromise even if OIDC tokens are protected?
Yes. If an API key is logged alongside session or token context, an attacker can correlate logs to impersonate users or call downstream services, bypassing OIDC protections that only guard authentication.
Does using PKCE with OpenID Connect prevent API key leakage in Phoenix?
PKCE protects the authorization code exchange but does not prevent API key leakage if the key is stored or logged server-side. Secure storage, strict logging hygiene, and server-side usage remain essential.