Api Key Exposure in Phoenix with Firestore
Api Key Exposure in Phoenix with Firestore — how this specific combination creates or exposes the vulnerability
When a Phoenix application uses Google Cloud Firestore and inadvertently exposes an API key, the combination expands the attack surface in ways specific to the Phoenix/Elixir runtime and Firestore’s permission model. An API key is a bearer credential; if it is embedded in client-side JavaScript, shipped in a public npm package, or logged by the Phoenix framework, an attacker who obtains it can use it to interact with Firestore directly. Because Firestore security rules are not a substitute for authentication, a valid API key can allow read or write access depending on the rules and indexes configured, enabling data exfiltration or injection.
In a Phoenix deployment, developers sometimes place Firestore configuration in runtime parameters or compile-time config files that are inadvertently included in assets. For example, placing the key in config/runtime.exs is safe only if the file is never served to the client; however, a misconfigured web server or a frontend build pipeline that bundles server-side configuration into JavaScript bundles can leak the key. Once leaked, the key maps to a project with Firestore instances that may have permissive rules for rapid prototyping, such as allowing read/write to authenticated users or, worse, allowing access based on request origin without proper condition checks.
Firestore’s rules operate on resource paths and can inadvertently grant broader access when API keys are used for authorization instead of proper IAM principals or Firebase Authentication. In an Elixir client, you might initialize Firestore using the Google API client library with an API key, which relies on project-level permissions. If the project has legacy IAM roles or Firestore rules that trust the API key context, an attacker can leverage endpoints such as Firestore’s REST or RPC APIs to enumerate collections, read sensitive documents, or execute writes. This is especially risky when combined with overly broad CORS rules in the hosting environment that allow any origin to include the key in requests, enabling cross-origin exploitation from a browser.
The attack chain often starts with discovery: scanning for references to firebase.googleapis.com or googleapis.com in JavaScript bundles, inspecting environment files in version control, or observing network requests from a Phoenix dev or staging build. Once the key is obtained, the attacker can use standard Firestore REST endpoints like projects/{projectId}/databases/(default)/documents:runQuery to test permissions. Common vulnerable patterns include using API keys with server-side SDKs in Phoenix controllers that proxy requests to Firestore, inadvertently turning the Phoenix server into a credential relay. MiddleBrick’s LLM/AI Security checks and input validation tests can detect exposed keys in responses and probe for insecure Firestore configurations, highlighting risks such as missing rule constraints or excessive entity permissions that amplify the impact of a leaked key.
Firestore-Specific Remediation in Phoenix — concrete code fixes
To reduce exposure, keep API keys out of client-side code and Phoenix assets entirely. Use server-side service accounts with limited IAM roles rather than API keys for Firestore access from Phoenix, and avoid passing sensitive configuration to the browser. When you must use API keys, restrict them tightly via application restrictions and API restrictions in the Google Cloud console, and ensure the key is only referenced in server-side configuration that is never bundled into frontend artifacts.
In Phoenix, store sensitive configuration in runtime files that are excluded from releases or ensure they are never served to the client. For example, configure Firestore access using a service account JSON mounted as a secret and accessed only on the server. Use the google_api_firestore library with a token obtained via impersonation or workload identity, rather than an API key, for operations initiated by Phoenix backends. Here is a safe server-side example using Application Default Credentials in Elixir, avoiding API keys in JavaScript:
# config/runtime.exs - server-side only
import Config
if config_env() == :prod do
config :my_app, MyApp.Firestore,
project_id: "#{System.get_env("FIRESTORE_PROJECT")}",
token_source: :service_account,
key_file: System.get_env("GOOGLE_APPLICATION_CREDENTIALS")
end
When initializing the Firestore client in your application supervisor, ensure the client is built only on the server and never serialized or logged:
# lib/my_app/firestore.ex
defmodule MyApp.Firestore do
use GoogleApi.Firestore.V1beta1.Connection
def start_link(_opts) do
# Build connection using Application.get_env for server-side config
project_id = Application.get_env(:my_app, __MODULE__)[:project_id]
token_source = Application.get_env(:my_app, __MODULE__)[:token_source]
key_file = Application.get_env(:my_app, __MODULE__)[:key_file]
# This is a simplified illustration; in production use a pool of connections
# and ensure credentials are protected.
conn = GoogleApi.Firestore.V1beta1.Connection.new(
Tesla.Client.new([{Tesla.Middleware.Header, {"x-goog-api-client", "elixir"}}]),
project_id: project_id,
token_source: token_source,
key_file: key_file
)
{:ok, conn}
end
end
On the Firestore side, adopt the principle of least privilege in security rules. Instead of relying on API keys to implicitly grant access, use authentication claims and scope rules tightly. For example, if you use Firebase Authentication, structure rules to validate authentication tokens and limit document access by UID:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /users/{userId} {
allow read, write: if request.auth != null && request.auth.uid == userId;
}
match /public/{document=**} {
allow read: if true;
allow write: if false;
}
}
}
Audit your Firestore rules and IAM bindings regularly. In the Google Cloud console, review which service accounts have roles such as roles/datastore.user or roles/datastore.owner, and remove unnecessary privileges. Rotate any exposed API keys immediately via the Google Cloud console and restrict their usage to specific IPs or referrers if they must be used temporarily for legacy integrations. MiddleBrick’s dashboard can help track risk scores over time, and the Pro plan’s continuous monitoring can alert you when risky configurations or exposed keys are detected in scans, enabling faster response.