KeyDrift
Free scan
← Leak scenarios

Claude Code left an OpenAI key in the browser bundle

Agents test what they build; sometimes the working version holds a live key. Runbook: rotate, relocate, re-scan. Free scanner included.

5 min read

Agents optimize for demonstrated success. When proving an integration works requires calling it — with a real key — some sessions leave that proof embedded in the artifact.

If your bundle now contains an OpenAI key nobody remembers adding, here is the likely session history and the response order.

What actually happened

A terminal session implemented an OpenAI feature end-to-end, including a live test call. Somewhere between debugging and commit, the key stopped being environment and started being content. Nothing errored on the way — that is what separates this failure from the ones your tooling catches.

  1. Integration attempted with env var; failed due to missing config in the run context.
  2. Agent hardcoded a key present in the session to unblock progress — a classic debugging shortcut.
  3. Test passed; cleanup intended but the line shipped in the committed diff.
  4. Build inlined or preserved the literal depending on placement; either way it reached served output.

Every step above is individually reasonable and none of them prints a warning. The value crosses into the bundle during substitution, not execution, so nothing in your runtime ever sees the moment it happened.

What someone can do with it

Session artifacts become public infrastructure for strangers:

  • Billable calls until rotation.
  • Potential admin-scope damage if the session held elevated keys.

Rotate first

Rotate before you touch any code. The key has been served to every visitor since the deploy that introduced it, cached by CDNs along the way, and very likely harvested by crawlers that scan served JavaScript for exactly these shapes. Revoke at platform.openai.com/api-keys immediately; audit recently created keys too. Removing the string from source does not un-publish it; only revocation closes the door.

A rotated key left in old deploys is still discoverable in CDN caches and archived copies. Rotation plus redeploy closes both halves; either alone leaves the door ajar.

Move the call somewhere the browser cannot read

The structural fix is always the same shape: the call moves to a context that holds the key without serving it, and the browser asks your server instead.

// committed during debugging:
const apiKey = "sk-proj-FAKE000000000000000000000";
// corrected: env-read on the server route
const apiKey = process.env.OPENAI_API_KEY;

Check whether yours is exposed

You can check manually right now: open the site, view source or open DevTools, and search the built JavaScript for sk-. A hit means the string shipped; decode or prefix-check it before deciding how bad the news is.

Manual searching proves one page on one day. KeyDrift fetches the deployment the way a browser would, follows chunks named only in route manifests, reads streamed hydration payloads, and classifies what it finds — secret, or public-by-design — so an anon key never shows up dressed as an emergency.

Keep it from coming back

One more thing worth knowing before you close the tab: fixing this once does not end the story. Agents imitate whatever pattern is already in the repo, templates carry their own defaults, and the next feature request can reintroduce the same shape of code. Continuous monitoring re-scans every deploy and alerts only on change — new, regressed, resolved — so the comeback attempt is a notification instead of a quarter-end surprise.

Reading your own history for other instances

Search the repo log for key-shaped strings (git log -p -S "sk-") and recent commits touching client files. Sessions that fixed one leak rarely fixed every instance they created.

Prevention that survives the next session

  • Scoped project keys with hard spend caps, so a leaked debug key costs little even if it slips.
  • Pre-deploy scanning as the backstop — agents will keep being agents.

The bigger picture

Zoom out and the pattern is bigger than one repo. AI-assisted output has outgrown review capacity everywhere at once, which means thousands of teams are making the same reasonable-looking tradeoffs in the same week. Claude Code users are not uniquely exposed — they are typically exposed. The failure mode documented above is the modal outcome of velocity without verification, not evidence of carelessness.

How KeyDrift reports this exact finding

Report anatomy matters during incidents, so it is worth reading once calmly: masked string (never the live value — it ceases to exist outside the detection engine), salted fingerprint (trackable within your workspace, useless to strangers), chunk path (your starting point for a "git log -S" hunt), disposition (secret versus public-by-design), confidence (matches below 0.5 never reach the page at all).

Manual check, step by step

The full manual drill, for readers who want zero dependence on any tool: open the deployed site in a private window; launch DevTools → Sources; use Search-all-files (Ctrl/Cmd+Shift+F) for sk-; then repeat for the other marker families — eyJ, sk_live_, sk-proj-, AKIA, postgres, BEGIN PRIVATE KEY. Decode anything JWT-shaped before reacting, and classify public-by-design formats as expected guests rather than intruders.

Close the loop with monitoring

If you take one operational step from this page, make it this: put the URL under continuous monitoring (free tier covers one project daily). The first scan tells you whether you have a problem today; the schedule tells you whether the problem comes back next month after someone re-adds the convenient line.

Common questions

The key worked in tests — doesn’t that mean it was intentional?

Intent is irrelevant to exposure; treat found literals as incidents regardless of origin story.

Should we stop using coding agents?

No — pair their velocity with deploy-side verification. The failure mode is structural, not moral.


Run a free scan at keydrift.dev/scan — paste a URL or the bundle source itself, no account. Findings arrive masked, with the exact chunk they live in.


KeyDrift is an independent product and is not affiliated with, endorsed by, or sponsored by Claude Code. The name is referenced descriptively.

Published by PostHat, KeyDrift’s content pipeline. Every factual claim is grounded in KeyDrift’s product documentation.