OpenAI and Anthropic API Keys Exposed in JavaScript

AI API keys embedded in client-side JavaScript grant full account access. How keys leak through bundlers, what attackers can do, and how to fix it.

AI API keys leaking through JavaScript bundler misconfiguration

An OpenAI or Anthropic API key in your frontend JavaScript is equivalent to handing out your account password. Anyone who opens the browser DevTools Sources panel or runs a two-line grep against your JS bundle can extract it and start making API calls billed to your account. The average time between a key being published and first unauthorized use is under four minutes, based on GitHub's leaked secret detection telemetry.

How Keys End Up in Client-Side JavaScript

  • Vite / Create React App env prefix mistake — Vite exposes any variable prefixed with VITE_ to the browser bundle. A developer who writes VITE_OPENAI_KEY=sk-... instead of OPENAI_KEY=sk-... ships the key in every page load.
  • Next.js publicRuntimeConfig — placing API keys under publicRuntimeConfig in next.config.js inlines them into the client bundle.
  • Direct SDK initialization in a component — calling new OpenAI({ apiKey: process.env.NEXT_PUBLIC_OPENAI_KEY }) in a React component. NEXT_PUBLIC_ prefixed variables are always public.
  • Hardcoded strings during prototypingconst client = new Anthropic({ apiKey: 'sk-ant-...' }) — committed and shipped before the prototype becomes production.
  • Source maps exposed — even if the key is obfuscated in the minified bundle, an exposed source map reconstructs the original variable names and string values.

What an Attacker Can Do With the Key

  • Run up charges — gpt-4o costs $5 per million input tokens. A scripted abuse loop can generate thousands of dollars in API charges within hours, with the bill going to the victim's account.
  • Access fine-tuned models — if the account has proprietary fine-tuned models, the key grants inference access to those models and their embedded training data.
  • Read assistants and files — the OpenAI Assistants API stores uploaded files and thread history under the account. A key with full scope reads all of it.
  • Extract system prompts via the API — Anthropic's Messages API and OpenAI's Chat Completions API return model responses that can be used to probe the system prompt boundaries of any assistant built on that key.
  • Pivot to organization data — organization-scoped keys on accounts with multiple projects expose all project API usage logs, member lists, and billing data via the admin API.

Detecting a Leaked Key

In the browser, open DevTools → Sources → search globally for sk-, sk-ant-, or OPENAI. Alternatively:

# Download the JS bundle and grep for key patterns
curl -s https://yoursite.com | grep -oE 'src="[^"]+\.js"' \
  | sed 's/src="//;s/"//' \
  | while read f; do
      curl -s "https://yoursite.com$f" | grep -oE '(sk-[a-zA-Z0-9]{32,}|sk-ant-[a-zA-Z0-9-]{50,})'
    done

# Check source maps too
curl -s https://yoursite.com/static/js/main.chunk.js.map \
  | grep -oE '(sk-[a-zA-Z0-9]{32,}|sk-ant-[a-zA-Z0-9-]{50,})'

You can also use the Secrets tab in browser DevTools (Chrome 124+) which flags high-entropy strings resembling API keys. For CI pipelines, truffleHog and gitleaks scan JavaScript build artifacts and source maps against known key patterns.

Remediation

  • Move all AI API calls to a backend — expose a thin proxy endpoint (/api/chat) that holds the key server-side and applies rate limiting and auth before forwarding to OpenAI or Anthropic.
  • Revoke and rotate immediately — if a key has been in a public JS bundle, assume it is compromised. Revoke it in the provider's dashboard before deploying the fix.
  • Use project-scoped keys — OpenAI project API keys are scoped to a single project and cannot access billing or organization settings, limiting blast radius.
  • Add pre-commit scanninggitleaks protect --staged blocks commits that include key patterns. Configure it as a git hook.
  • Never use NEXT_PUBLIC_ or VITE_ prefixes for secrets — these prefixes exist specifically to expose values to the browser. Any variable that should be private must not have them.

Orb44 Detection

Orb44 scans your site's JavaScript bundles and source maps for exposed credentials including AI provider API keys. Leaked keys surface as high-severity credential findings on your dashboard. Check your site at orb44.com.

Common Mistakes

  • Thinking minification hides the key — minification renames variables but does not encrypt string literals. The key is still there, just slightly harder to spot manually.
  • Restricting the key by domain in the provider dashboard — domain restrictions apply to browser-enforced CORS, not to direct API calls. An attacker calling the API from curl bypasses them entirely.
  • Fixing the leak in the latest deployment but leaving old bundles cached in a CDN or archived in the Wayback Machine.

FAQ

Can I use a restricted API key that only allows certain models?

OpenAI project keys can be scoped by API surface but not by model. An attacker with the key can still call any model your account has access to and run up charges. There is no safe way to put an API key in client-side code.

How fast will I know if my key is abused?

OpenAI and Anthropic send usage alerts when spending exceeds a threshold, but the default alert is set in dollars, not requests. A burst of cheap calls may not trigger an alert for hours. Monitor usage in real time via the usage dashboard and set low alert thresholds.

Does GitHub's secret scanning cover AI API keys?

Yes. GitHub secret scanning detects OpenAI and Anthropic key patterns in pushed commits and can automatically notify the provider to revoke them. Enable it in your repository's security settings. This covers source code but not built JS bundles deployed separately.

Orb44 Journal · all posts