Example finding How it works Coverage PENTEST METHODOLOGY DOCS PRICING FAQ MCP CONTACT LOG IN SIGN UP →

API Token Leak Checker: Scan Exposed Keys in Frontend Code

Paste a URL. The scanner loads your live site and reads every JS bundle the browser fetched, fingerprinting 100+ key formats. If an attacker can open DevTools, they can see what we see.

FIND LEAKED API KEYS NOW

Enter your URL - we load the bundle in a headless browser and fingerprint Firebase, Stripe, AWS, OpenAI, Anthropic, Supabase, GitHub and 100+ more in under 30 seconds.

What is an API token leak?

An API token leak is when an API key, secret, or access token ends up inside code that every visitor can download - usually the JavaScript bundle. Anyone who opens browser DevTools can copy the key and use it from their own machine. Automated bots do this at scale: they crawl deployed sites, GitHub, and npm packages looking for known key formats, and they find new ones every minute.

This scanner finds those keys in under 30 seconds so you can rotate before a bot does. It is intentionally narrow: credentials in the browser surface. Pair it with the Vibe Code Scanner for authz and open-database classes that are not “keys” but still dump data.

Why token leaks happen to AI-generated apps

AI coding tools - Lovable, Bolt, v0, Cursor, Claude Code, Replit - default to importing client SDKs directly into the frontend. That pattern ships your API key to every visitor. Keys that vendors label “safe for the client” (Stripe publishable, Supabase anon, Firebase config) are still abuse surfaces if the rest of your security model assumes no one has them. Keys that should never have shipped - OpenAI, Anthropic, Twilio, server-only Stripe - are catastrophic: one screenshot, one curl, and an attacker is billing your card.

Most of the vibe-coded apps we scan have at least one key in the frontend bundle that shouldn’t be there. See OWASP Top 10 for AI-generated code for the full pattern (especially A02 Cryptographic Failures).

How the generator creates the leak

  1. You prompt “add Stripe payments” or “call OpenAI for chat.”
  2. The model pastes the SDK example, which often includes process.env.STRIPE_SECRET_KEY or a hard-coded sk-... placeholder.
  3. In Vite/Next, someone renames the var to VITE_ / NEXT_PUBLIC_ so “it works in the browser.”
  4. The build inlines the value into a chunk. Deploy. Bots find it within hours.

The same loop produces service_role Supabase keys, Firebase service-account JSON, and AWS AKIA keys in public/ folders.

Host-specific accelerants

  • Vercel / Next: NEXT_PUBLIC_ prefix is the classic footgun (Is Vercel Safe?)
  • Vite / Bolt: VITE_ inlining
  • Webflow custom code: secrets pasted into site-wide scripts (Is Webflow Safe?)
  • Replit: secrets accidentally written into client files (Is Replit Safe?)
  • Source maps: readable originals that make keys trivial to extract (Source Map Checker)

What the scanner checks

FIREBASE / FIRESTORE

API keys, project IDs, service account JSONs, and storage bucket credentials accidentally bundled into the client.

STRIPE

Secret keys (sk_live_…) in the frontend where only publishable keys belong, plus webhook secrets.

AWS / GOOGLE CLOUD

Access key IDs, secret keys, session tokens, signed URLs, and service-account JSON.

OPENAI / ANTHROPIC

LLM provider keys - usually mean you have exposed pay-per-token billing to the internet.

SUPABASE

Service-role keys where only the anon key belongs, and anon keys in front of tables with no RLS.

GITHUB / CI

Personal access tokens, fine-grained tokens, and CI secrets leaked through source maps or error pages.

Additional signatures typically include SendGrid, Twilio, Slack, Mapbox, and high-entropy bearer tokens. Treat any unexpected high-entropy string in a public chunk as guilty until proven to be a public client ID.

How it works

  1. Load - we fetch your URL in a real headless browser, the same way a user would.
  2. Capture - every JS file, inline script, source map, and XHR response gets inspected.
  3. Pattern match - 100+ known key signatures plus entropy-based detection for custom secrets.
  4. Report - each finding shows the source file, line number, key type, and remediation steps.

Because the browser is real, dynamically imported chunks and lazy routes still get pulled if the page loads them. For apps that only inject secrets after a deep navigation, also exercise those routes or run a fuller app scan.

Which keys are safe in the frontend?

Key Safe in browser? Why
Stripe publishable (pk_live_…) Yes Designed to ship; restrict domain in Stripe dashboard.
Stripe secret (sk_live_…) No Full account access. Server-only.
Firebase Web config Conditional Safe only if Firestore Security Rules and Auth are correctly configured.
Firebase service account No Admin access to the entire project.
Supabase anon key Conditional Safe only if Row Level Security is enforced on every table.
Supabase service_role key No Bypasses RLS. Server-only.
OpenAI / Anthropic API key No Direct billing access. Always proxy through a backend.
AWS access key No Console and API access. Never ship to the browser.
Google Maps JS API key Yes Lock by HTTP referrer in Google Cloud Console.
GitHub PAT No Repo and org access. Never ship.
Slack bot token No Workspace access. Server-only.

“Conditional” keys still need abuse controls: rate limits, referrer restrictions, and correct server-side policies. Public ≠ free-for-all.

Impact of the worst leaks

Stripe sk_live_: attacker creates charges, refunds, exports customers, changes payouts.

OpenAI / Anthropic: attacker burns your quota on cryptomining-style completions; bills hit five figures overnight without a hard cap.

Supabase service_role: full database dump/modify/delete; RLS is irrelevant.

AWS AKIA + secret: depending on IAM, full account takeover, crypto miners in your regions, public S3 dumps.

GitHub PAT: push malware to your repos, steal other secrets from Actions, pivot into org SSO apps.

Twilio / SendGrid: spam campaigns from your brand, phone fraud, reputation burn.

Rotate first, argue about severity later. Minutes matter more than perfect classification.

Common fixes

  • Move keys to server-side code, edge functions, or backend proxies. The client should never hold a secret.
  • For keys that must ship (Stripe publishable, Google Maps), restrict by origin/referrer in the provider dashboard.
  • Rotate any key that ever appeared in a past deploy, GitHub commit, or log - bots already indexed it.
  • Add a CI check that fails the build when a new token exposure is introduced.
  • For Supabase, turn on RLS on every table before worrying about the anon key - RLS is the actual auth.
  • Disable public source maps in production.
  • Strip secrets from error pages and client-visible config endpoints.

Proxy pattern (Edge Function)

// Keep OPENAI_API_KEY only in server env
export async function POST(req: Request) {
  const session = await getSession(req);
  if (!session) return new Response("Unauthorized", { status: 401 });

  const { prompt } = await req.json();
  // rate-limit + validate prompt length here
  const r = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
      "content-type": "application/json",
    },
    body: JSON.stringify({
      model: "gpt-4o-mini",
      messages: [{ role: "user", content: String(prompt).slice(0, 4000) }],
    }),
  });
  return new Response(r.body, { headers: { "content-type": "application/json" } });
}

Local grep before every deploy

grep -rE 'sk_(live|test)_|sk-[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|xox[baprs]-' \
  dist/ .next/static/ --include='*.js' || true

Any hit in dist/ or .next/static/ is a ship blocker.

CI gate sketch

- name: Fail on secret-shaped strings in build
  run: |
    if grep -rE 'sk_live_|sk_test_|service_role|AKIA[0-9A-Z]{16}' .next/static dist 2>/dev/null; then
      echo "Possible secret in client build"
      exit 1
    fi

How to verify after rotation

  1. Rotate the key in the provider console (disable the old one completely).
  2. Remove the public prefix / move the call behind a server route.
  3. Rebuild with a clean cache (rm -rf .next dist && npm run build).
  4. Redeploy.
  5. Re-run this Token Leak Checker on the production URL.
  6. Search git history (gitleaks detect --log-opts="--all") - if the key was committed, treat it as permanently public even after rewrite unless you rotate.
  7. Check provider audit logs for usage from unexpected IPs between leak and rotation.
  8. Scan preview domains too - they often retain the old inlined values longer than you think.

Source maps: the silent amplifier

Production source maps (.js.map) make leaks easier to read and sometimes contain strings stripped from minified bundles. Disable public source maps for production builds, or host them behind auth for error-tracking only. The Source Map Checker flags public maps.

// next.config.js sketch
module.exports = {
  productionBrowserSourceMaps: false,
};

What this scanner does not replace

  • RLS / Security Rules - a public anon key with open tables is a data breach even when no “secret” key is present. Use the Supabase RLS Checker and Firebase Scanner.
  • Server-side secrets in CI logs - we only see the browser surface.
  • Keys behind login returned by APIs - run authenticated testing for those.
  • Mobile app binary strings - different packaging; use mobile-specific secret scanning.
  • Business-logic authz - BOLA is not a token leak; use full app scanning.

Incident playbook (first hour)

  1. Identify which key types appeared and in which chunk/URL.
  2. Rotate / revoke those keys in provider dashboards immediately.
  3. Kill billing risk: hard cap OpenAI/Anthropic; pause Stripe if needed.
  4. Remove public prefixes and redeploy clean builds.
  5. Audit logs for abuse between first deploy and rotation.
  6. Notify stakeholders if customer data or payment rails were exposed.
  7. Prevent with CI grep + ignore files + developer education on NEXT_PUBLIC_.

Do not wait for a perfect root-cause document before rotating.

Common questions

What is an API token leak?
An API token leak is an API key, secret, or access token that has been shipped into code visible to end users - typically the JavaScript bundle downloaded by every visitor. Anyone who opens browser DevTools can copy the key and use it from their own machine.
Can attackers really see API keys in my frontend code?
Yes. Any key that reaches the browser - whether inlined in HTML, bundled into JavaScript, or returned by an API call - is readable by anyone. View-source and DevTools make it trivial. Obfuscation and minification do not hide keys from automated scanners; bots index GitHub, npm, and deployed sites continuously for known key formats.
What keys does the scanner detect?
Firebase API keys and service accounts, Stripe publishable and secret keys, AWS access keys and session tokens, OpenAI and Anthropic LLM keys, Supabase anon and service-role keys, GitHub personal access tokens, Google Maps and Cloud keys, Mapbox tokens, SendGrid keys, Twilio keys, generic JWTs, and high-entropy strings. Over 100 signatures.
What's the difference between a Stripe publishable key and a secret key?
The publishable key (pk_live_…) is designed to ship to the browser - it can only create tokens, not charge cards. The secret key (sk_live_…) can charge, refund, and list all customers. If a secret key appears in your frontend bundle, an attacker has full control of your Stripe account.
Is the Supabase anon key safe to expose?
Only if Row Level Security (RLS) is enabled on every table and the policies are correct. The anon key by itself is designed to be public, but without RLS it becomes a read/write key for your entire database. Most 'Supabase was hacked' stories are missing or broken RLS, not leaked keys.
Do I need to rotate a leaked key even if no one accessed it?
Yes. Public repos, deployed bundles, and crawled pages are continuously scraped by credential-harvesting bots. If a key has been public for even a few minutes, assume it is in at least one attacker's wordlist. Rotate immediately, then audit logs from that key for anomalies.
Is the scan safe to run on production?
Yes. The scanner loads the same URL a normal visitor would and inspects the JavaScript that was already sent to them. No additional traffic, no auth bypass, no stored results. It is a read-only fetch against your public frontend.
How do I fix a leaked key?
Rotate the key immediately in the provider dashboard (Stripe, Firebase, Supabase, OpenAI). Move the integration behind a backend proxy or edge function so the key never ships to the browser. Rebuild and redeploy the frontend. Then search git history - if the key was ever committed, it is compromised forever.
Can minification or obfuscation hide keys?
No. Minifiers rename variables; they do not encrypt string literals. Automated scanners match known prefixes (sk_live_, sk-, AKIA, eyJ) and high-entropy strings regardless of surrounding code shape. Treat obfuscation as zero security for secrets.
What about keys only loaded after login?
If a secret is returned by an API to the browser after authentication, it is still a leak - any logged-in user (or XSS on that session) can exfiltrate it. Secrets that must never leave a trusted server should never be in a JSON response either.

Keys clean? test the rest

Token leaks are critical - but open databases and broken auth sink apps too. Run the full agent on the same URL.

14-day free trial · No credit card · Cancel anytime

RUN FULL SCAN