A startup's OpenAI bill ran at $400/month for months. One public GitHub commit exposed the key for 11 days. The next invoice was $67,000.
LLM API keys are categorically different from traditional credentials. A stolen key converts directly to billable compute with no further exploitation required. The standard secret hygiene guide has not caught up with the 5 exposure vectors the AI tooling ecosystem created.
LLM Keys Have an Immediate Financial Blast Radius That Generic API Keys Do Not
Stolen database credentials require an attacker to extract data before causing financial harm. S3 keys need write permissions to generate impact. A stolen LLM API key starts generating cost on the first request.
Sysdig documented LLMjacking in May 2024. Attackers used CVE-2021-3129, a Laravel RCE, to steal cloud credentials and probe 10 AI providers simultaneously. The documented potential cost was $46,000 per day burning Claude 2 quota. In January 2025, Microsoft sued Storm-2139, a criminal syndicate that industrialized the attack at scale. Simultaneous targets included Azure, OpenAI, Anthropic, AWS Bedrock, Google Vertex AI, and Mistral.
OpenAI and Anthropic keys do not expire by default. A key leaked in 2024 works in 2026 unless manually revoked. No data needs to be stolen for the damage to be real: $50,000 in compute is the damage.
Five Exposure Vectors the AI Tooling Ecosystem Created
Standard hygiene guides cover .env files in repositories, hardcoded variables in source, and committed config files. The AI tooling ecosystem created 5 new surfaces those guides have not addressed.
Jupyter notebooks (.ipynb): The format saves cell outputs by default. A cell containing openai.api_key = 'sk-...' followed by print(client.api_key) persists the value in the file's JSON. Any git add notebook.ipynb includes the key in history.
MCP config files: GitGuardian found 24,008 unique secrets in MCP configuration files on public GitHub in 2025. Of those, 2,117 were valid, an 8.8% validity rate. Files like ~/.claude/mcp.json and .cursor/mcp.json live in dotfiles that developers commit alongside other configuration.
Frontend bundles (Vite, CRA, Next.js): Variables prefixed with VITE_, REACT_APP_, and NEXT_PUBLIC_ are injected into the bundle at build time. A key in VITE_OPENAI_KEY becomes a literal string in the .js file served publicly.
Docker build args: docker build --build-arg OPENAI_KEY=sk-... records the value in image metadata. Running docker history exposes all ARGs per layer. The value is permanently baked into the intermediate layer.
AI-assisted commits: GitGuardian (2026) measured that commits generated with AI coding tool assistance leak secrets at 3.2%, compared to 1.5% in the baseline without assistance. Tools that write code suggest patterns without checking where keys will be stored.
Framework Env Var Prefixes Are Publication Flags, Not Security Boundaries
VITE_, REACT_APP_, and NEXT_PUBLIC_ are documented mechanisms to inject values into client-side bundles. Developers read these prefixes as scoping modifiers. They are publication flags.
Vite replaces import.meta.env.VITE_OPENAI_KEY with the literal value at build time. The minifier preserves the value and discards the variable name. The key is readable in any user's DevTools.
In Next.js, NEXT_PUBLIC_ works identically at build time. The subtler trap: passing an LLM key as a prop from a Server Component serializes it into __NEXT_DATA__ JSON in the HTML. The prefix is not even required for this to happen.
Deleting a File Does Not Delete a Secret
git rm .env removes the file from the working tree. The key remains in every prior commit in the history. Any clone of the repository contains the secret.
Running git log -p --all -S 'sk-' recovers deleted keys from the complete history. GitHub indexes public repository history: a secret committed then deleted stays searchable until the history is rewritten or the repository goes private.
The same logic applies to Docker layers. RUN rm /app/.env removes the file from that layer's filesystem, but docker history exposes the ARG and every intermediate layer remains accessible. The fix is not deletion: use BFG Repo Cleaner to rewrite git history and rebuild the image with docker build --no-cache after removing the ARG.
Usage Spike Detection Is the Earliest Signal Once the Key Is Out
Secret scanning finds keys before or shortly after exposure. Usage monitoring is the only signal that works when the key is already circulating. A 100x spike in token consumption within one hour is the most reliable indicator of compromise.
The OpenAI dashboard shows per-project consumption. A spike in the last hour versus the 30-day average is the first warning in production. On AWS Bedrock, anomalous InvokeModel calls via CloudTrail from unexpected IPs confirm abuse. Sysdig recorded that attackers used the -1 token parameter to probe access without triggering per-request alarms.
The most effective prevention is a pre-commit hook with regex for key formats: sk-[a-zA-Z0-9]{48} (OpenAI legacy), sk-proj-[A-Za-z0-9]{48,} (OpenAI current), sk-ant-api03-[A-Za-z0-9_-]{95} (Anthropic), AIza[0-9A-Za-z_-]{35} (Google).
The MAGO Intel tool (intel.mago.team) scans public repositories and build artifacts for exposed LLM API keys. It covers MCP config files, Jupyter notebooks, and compiled frontend bundles. Each finding includes provider attribution and first-seen timestamp.
Rotate First, Investigate Second: The 15-Minute Playbook
Bots scan new public GitHub commits continuously. In the documented $67,000 case, bots found the key within minutes of the commit. Response order matters: revocation before investigation stops the financial bleeding.
OpenAI: dashboard.openai.com > API Keys > Revoke. Create the replacement key before revoking the compromised one to avoid downtime.
Anthropic: console.anthropic.com > API Keys > Delete. Confirm via curl https://api.anthropic.com/v1/models returning 401.
Google: cloud.google.com > APIs & Services > Credentials > Disable key.
After rotation: rewrite git history with BFG Repo Cleaner (java -jar bfg.jar --replace-text passwords.txt). Run a forced push to update the remote, rebuild Docker images with --no-cache, and notify all downstream consumers. Then investigate: which commits, which repositories, which time window, whether billing events occurred in that window.
The pattern is consistent: developers reach for LLM APIs in environments where they apply experiment thinking to production security. Tool defaults point toward exposure. Fix the defaults: proxy LLM calls through a backend and add pre-commit hooks for key patterns. Set a per-project spend alert that fires before the damage compounds.
Top comments (0)