HIGH api key exposurephoenix

Api Key Exposure in Phoenix

Phoenix-Specific Remediation

Remediating API key exposure in Phoenix applications requires systematic changes to how secrets are handled. The first step is implementing proper secret management using Phoenix's configuration system. Move all API keys to environment variables and use Phoenix's runtime configuration:

# config/runtime.exs
import Config

config :my_app, MyApp.API,
  stripe_key: System.get_env("STRIPE_API_KEY") || raise "STRIPE_API_KEY not found",
  aws_key: System.get_env("AWS_ACCESS_KEY_ID") || raise "AWS_ACCESS_KEY_ID not found"

This approach ensures that missing keys cause application startup failures rather than silent fallbacks to insecure defaults.

Implement request filtering in Phoenix controllers to prevent API key leakage through error responses:

defmodule MyAppWeb.Plugs.APIFilter do
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _opts) do
    conn
    |> put_private(:sensitive_keys, ["api_key", "password", "secret"])
    |> register_before_send(&filter_response/1)
  end

  defp filter_response(conn) do
    if conn.status >= 400 do
      body = conn.resp_body || ""
      filtered_body = body
                    |> Jason.decode!()
                    |> filter_sensitive_data()
                    |> Jason.encode!()
      %{conn | resp_body: filtered_body}
    else
      conn
    end
  end

  defp filter_sensitive_data(map) when is_map(map) do
    Map.new(map, fn
      {key, value} when key in ["api_key", "password", "secret"] ->
        {key, "[FILTERED]"}
      {key, value} when is_map(value) ->
        {key, filter_sensitive_data(value)}
      {key, value} ->
        {key, value}
    end)
  end

  defp filter_sensitive_data(list) when is_list(list) do
    Enum.map(list, &filter_sensitive_data/1)
  end

  defp filter_sensitive_data(other), do: other
end

Add this plug to your Phoenix router to automatically filter sensitive data from error responses.

Secure your Phoenix application's logging configuration by implementing structured logging with sensitive data filtering:

defmodule MyAppWeb.Plugs.SecureLogger do
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _opts) do
    conn
    |> put_private(:log_filter, fn data -> filter_log_data(data) end)
    |> register_before_send(fn conn ->
      Logger.info("Request completed: #{conn.method} #{conn.request_path}")
      conn
    end)
  end

  defp filter_log_data(data) do
    Enum.reduce(data, data, fn {key, value}, acc ->
      if String.contains?(key, "password") or String.contains?(key, "secret") do
        Map.put(acc, key, "[FILTERED]")
      else
        acc
      end
    end)
  end
end

Implement comprehensive testing for API key exposure using Phoenix's testing framework:

defmodule MyAppWeb.APITest do
  use MyAppWeb.ConnCase
  import Phoenix.ConnTest

  setup do
    api_key = "sk_test_1234567890"
    {:ok, api_key: api_key}
  end

  test "does not expose API keys in error responses", %{conn: conn, api_key: api_key} do
    conn =
      conn
      |> put_req_header("authorization", "Bearer #{api_key}")
      |> post("/api/v1/endpoint", %{invalid: "data"})

    assert json_response(conn, 400)["error"] != api_key
    refute String.contains?(conn.resp_body, api_key)
  end

  test "logs do not contain sensitive data", %{conn: conn, api_key: api_key} do
    # This test would verify that logging doesn't capture sensitive data
    # Implementation depends on your logging setup
    assert true
  end
end

Finally, implement runtime monitoring in your Phoenix application to detect potential API key exposure:

defmodule MyApp.Monitoring do
  use GenServer

  def start_link(_opts) do
    GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
  end

  def init(:ok) do
    schedule_scan()
    {:ok, %{}}
  end

  def handle_info(:scan_for_exposure, state) do
    # Implement scanning logic here
    # Check logs, responses, error messages for exposed keys
    schedule_scan()
    {:noreply, state}
  end

  defp schedule_scan do
    Process.send_after(self(), :scan_for_exposure, 60_000) # Scan every minute
  end
end

Register this monitoring process in your Phoenix application supervisor to continuously check for potential API key exposure.

Frequently Asked Questions

How can I prevent API keys from being committed to my Phoenix repository?
Use environment variables for all API keys and add configuration files containing secrets to your .gitignore. Implement pre-commit hooks using tools like husky or pre-commit to scan for potential secrets before commits are allowed. Phoenix's runtime configuration system (config/runtime.exs) is designed specifically for this purpose.
What's the best way to handle API keys in Phoenix test environments?
Use mock implementations or test-specific API keys in your Phoenix test configuration. Create a separate config/test.exs file that defines test doubles for external services. Never use production API keys in test environments, and consider using tools like Mox or Mock for creating test doubles of external service clients.