Api Key Exposure in Phoenix with Api Keys
Api Key Exposure in Phoenix with Api Keys — how this specific combination creates or exposes the vulnerability
In Phoenix applications, exposing API keys in client-side code or configuration files reachable from the web creates a direct path for unauthorized access and abuse. When API keys are embedded in JavaScript, templates, or environment variables that leak to the browser, any attacker can harvest them and use them to call downstream services, bypass intended rate limits, or exceed quota.
Phoenix endpoints that render configuration values into views or API responses without filtering sensitive fields can unintentionally include keys in JSON payloads served to unauthenticated clients. For example, a debug endpoint returning the entire runtime configuration or a health check that echoes connection parameters may include keys if developers are not careful. Because these endpoints are often unauthenticated or lightly protected, an attacker can probe standard paths such as /debug, /health, or /config to locate keys.
The risk is compounded when keys have broad permissions or are long-lived, resembling secrets stored in plaintext rather than references to secure vaults. An exposed key allows an attacker to act with the same permissions as the service that used it, leading to data exfiltration, unauthorized transactions, or lateral movement within your infrastructure. middleBrick’s Data Exposure and Property Authorization checks specifically flag endpoints that return sensitive server-side values, including patterns that match known key formats, so you can identify and remediate these leaks before an attacker finds them.
During a black-box scan, middleBrick tests unauthenticated attack surfaces and runs checks in parallel, including Data Exposure, to detect whether API keys appear in responses. This is distinct from authentication flaws; even properly authenticated endpoints can leak keys if they include them in payloads or logs that reach the client. Because scanning takes 5–15 seconds, you can quickly validate whether key material is exposed and prioritize fixes based on severity.
Api Keys-Specific Remediation in Phoenix — concrete code fixes
To prevent API key exposure in Phoenix, keep keys out of the client-side and ensure runtime configuration filters sensitive values. Use server-side environment variables and inject only non-sensitive configuration to controllers and views. Never pass raw keys or full configuration maps to templates or JSON responses.
1. Filter keys in controllers and views
When returning configuration for debugging or operational endpoints, explicitly exclude sensitive fields. Instead of dumping the entire configuration map, select only safe keys.
defmodule MyAppWeb.DebugController do
use MyAppWeb, :controller
def show(conn, _params) do
config = Application.get_all_env(:my_app)
safe_config = Map.take(config, [:app_name, :endpoint_port, :log_level])
json(conn, %{config: safe_config})
end
end
2. Use runtime configuration with restricted keys
Configure services using system environment variables and read them at startup, then pass derived, non-sensitive values to your application modules. Avoid storing keys in config files that might be checked into version control or exposed via debug routes.
config :my_app, MyApp.ServiceClient,
api_key: System.get_env("SERVICE_API_KEY")
# In your client module, use the key internally without exposing it
defmodule MyApp.ServiceClient do
@api_key Application.get_env(:my_app, __MODULE__)[:api_key]
def call_external do
headers = [{"Authorization", "Bearer #{@api_key}"}]
# perform request internally
end
end
3. Protect debug and health endpoints
Ensure that endpoints which could echo configuration are either removed in production or protected with authentication and strict authorization. If you must keep them, filter out sensitive keys and restrict access to internal networks or admin roles.
defmodule MyAppWeb.HealthController do
use MyAppWeb, :controller
plug MyAppWeb.Plugs.Authorize when action in [:show]
def show(conn, _params) do
# Return only status, not configuration
json(conn, %{status: "ok"})
end
end
4. Validate and sanitize logs
Ensure your logging configuration does not include API keys. If structured logging is used, redact values that match key patterns before writing events.
config :logger, :console,
format: "$time $metadata[$level] $message\n",
metadata: :redaction_fn
defmodule MyAppWeb.Plugs.RedactKeys do
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
# Example redaction hook; implement pattern replacement for keys
assign(conn, :redaction_fn, &redact/1)
end
defp redact(msg) do
# Replace API key-like strings in messages
Regex.replace(~r/API_KEY=[^\s&\"]+/, msg, "API_KEY=[REDACTED]")
end
end
5. Use vault integration for runtime secrets
For production, retrieve keys from a secure vault at runtime rather than embedding them in releases or config files. Your application should request tokens at startup and keep them in memory, avoiding any controller or view exposure.
defmodule MyApp.SecretProvider do
def get_api_key do
# Example: fetch from a vault, cache in application env
case Cachex.get(:secret_cache, :service_key) do
{:ok, key} -> key
:not_found -> fetch_from_vault_and_cache()
end
end
defp fetch_from_vault_and_cache do
key = VaultClient.get("secret/data/service")["data"]["api_key"]
Cachex.put(:secret_cache, :service_key, key)
key
end
end