Api Key Exposure in Phoenix with Dynamodb
Api Key Exposure in Phoenix with Dynamodb — how this specific combination creates or exposes the vulnerability
When a Phoenix application stores or references API keys in DynamoDB and then uses those keys in client-side or server-side logic, the risk of exposure increases through common web development patterns. DynamoDB, as a persistent NoSQL store, is often used to cache secrets for downstream services. If API keys are stored without encryption-at-rest protections outside of DynamoDB controls or are embedded in responses that reach the client, they can be inadvertently exposed.
In a typical Phoenix setup, developers may retrieve an API key from DynamoDB to sign requests to a third-party payment gateway or external API. If the DynamoDB table has a misconfigured IAM policy, the table may allow unauthenticated read access or broad list access, enabling an attacker to enumerate stored keys. Even when IAM is correctly configured, a path traversal or IDOR bug in the Phoenix controller could allow an authenticated user to query for keys that belong to other tenants or services, effectively turning DynamoDB into a leak channel.
Additionally, logging practices in Phoenix can amplify exposure. If the application logs the retrieved API key—intentionally or unintentionally—those logs may be stored on disk or shipped to monitoring systems. Combined with DynamoDB streams or backups, an attacker who gains access to logs or backup snapshots can recover live keys. The risk is further compounded when API keys are passed to JavaScript in the browser for signing or authorization, because client-side code cannot securely hold long-lived secrets.
Another vector involves DynamoDB secondary indexes and query patterns. Developers sometimes project sensitive attributes into Global Secondary Indexes for performance, inadvertently making keys searchable and enumerable. If the Phoenix application exposes an endpoint that queries such indexes without strict authorization, the keys can be enumerated at scale. This aligns with common OWASP API Top 10 risks such as Broken Object Level Authorization (BOLA) and excessive data exposure, where access controls are either missing or bypassed.
Using middleBrick, teams can scan their Phoenix endpoints that interact with DynamoDB to detect whether API keys are exposed in responses, whether endpoints lack proper authentication, or whether the API surface includes unauthenticated paths that reference sensitive data. The scanner checks for unauthenticated LLM endpoints and output patterns that may reveal keys or other sensitive material, helping to catch misconfigurations before they reach production.
Dynamodb-Specific Remediation in Phoenix — concrete code fixes
Remediation focuses on ensuring API keys are never returned to the client, are encrypted at rest and in transit, and are accessed only through tightly scoped, least-privilege patterns. Use DynamoDB encryption features and avoid selecting sensitive attributes in queries. In Phoenix/Elixir, structure your data access to exclude secrets from responses and logs.
Example: Safe DynamoDB read with field exclusion
Ensure your DynamoDB queries project only the fields you need and never include the key itself. Use expression attribute names to avoid accidental exposure through attribute aliasing.
defmodule MyApp.Dynamo do
import ExAws.Dynamo
alias ExAws.Dynamo.{Scan, Query}
# Safe: select only non-sensitive fields
def list_services(table_name) do
table_name
|> scan()
|> select([:service_name, "endpoint_url", "created_at"])
|> expression_attribute_names(%{"#url" => "endpoint_url"})
|> ExAws.request!()
end
end
Example: Avoid returning keys in controller
In your Phoenix controller, never render API keys. If you must use a key to call an external service, pass it directly to the client library and ensure it is not included in assigns or view templates.
defmodule MyAppWeb.ServiceController do
use MyAppWeb, :controller
alias MyApp.Dynamo
def show(conn, %{"id" => id}) do
case Dynamo.get_service(id) do
{:ok, %{endpoint_url: url, http_method: method}} ->
# Use the key internally only; do not assign or render it
client = MyApp.ExternalClient.new(url, method)
result = MyApp.Service.call(client)
render(conn, "show.json", result: result)
{:error, :not_found} ->
send_resp(conn, 404, "Not found")
end
end
end
Example: Secure DynamoDB table policy and IAM
Define a tight IAM policy that denies ListScan and GetItem for non-application roles and conditionally allows access only when encryption context matches. In Elixir, use environment-based configuration to avoid committing credentials.
# config/runtime.exs
import Config
config :my_app, :dynamodb,
table: System.fetch_env!("DYNAMODB_TABLE"),
encryption_enabled: true,
kms_key_id: System.fetch_env!("KMS_KEY_ID")
# Use IAM role-based credentials with least privilege; do not embed keys
Example: Parameterized queries to prevent IDOR
Use parameterized queries rather than string interpolation to prevent attackers from manipulating keys or indexes. Validate ownership on every request to enforce BOLA protections.
def get_key_for_user(user_id, key_id) do
table_name = "api_keys"
key_condition = {user_id: user_id, key_id: key_id}
ExAws.Dynamo.get_item(table_name, key_condition)
|> ExAws.request!()
|> Map.get("Item")
|> Map.get("encrypted_key")
end
Example: Use Phoenix endpoint protections
Add plugs that strip sensitive fields from assigns before rendering and ensure no logging of secret values. Combine with runtime encryption and strict CORS policies to reduce exposure surface.
defmodule MyAppWeb.Plugs.SensitiveDataFilter do
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
# Ensure API keys are not in assigns
assign(conn, :filtered, fn ->
Map.drop(conn.assigns, [:api_key, :secret])
end)
end
end
By combining these patterns—selective projection, strict IAM, runtime encryption, and disciplined controller logic—you reduce the likelihood that API keys stored in DynamoDB become exposed through the Phoenix application layer.