Exposed API Keys in JavaScript: How Hackers Find Your Secrets

Published December 9, 2025 · 8 min read

Exposed API Keys in JavaScript: How Hackers Find Your Secrets

Here's something that keeps me up at night: I scan random sites from Product Hunt launches and Hacker News "Show HN" posts. About 1 in 5 has at least one exposed API key sitting in their JavaScript bundle. Just... out there. For anyone to grab.

And attackers do grab them. There are bots constantly crawling the web, running regex patterns against every JavaScript file they can find. The moment they hit a valid AWS key or Stripe secret, it gets exploited within minutes. Not hours. Minutes.

What Actually Gets Exposed

Your JavaScript bundle is public. Anyone can view it. When you accidentally include secrets, here's what they find:

The expensive ones: The dangerous ones: The annoying ones:

How They Find Them

It's not sophisticated. The tooling is trivially available.

Pattern matching. Simple regex against your JavaScript:

\\\

AKIA[0-9A-Z]{16} # AWS

sk_live_[a-zA-Z0-9]{24} # Stripe

sk-[a-zA-Z0-9]{48} # OpenAI

ghp_[a-zA-Z0-9]{36} # GitHub

\\\

Source map hunting. If you accidentally ship .map files, attackers get your original source code. Makes finding secrets trivial. Variable name scanning. Search for assignments to variables named apiKey, secret, token, password, credential. You'd be surprised how often this works.

Real Damage I've Seen

A friend shipped an AWS key in a React app. Within 4 hours, someone had spun up EC2 instances for crypto mining. The bill was $12,000 before AWS fraud detection caught it.

Another indie hacker had their Stripe test key in production code. Except it wasn't a test key - it was live. Attacker issued refunds to themselves through a compromised customer account.

OpenAI keys are particularly popular right now. There are entire Discord servers trading them. Your key gets shared, a hundred people start making GPT-4 calls, and suddenly you've got a $2,000 bill.

How to Check Your Site

The manual way:
  • Open your production site
  • View Page Source (or DevTools > Sources)
  • Ctrl+F for: AKIA, sk_live, sk_test, sk-, ghp_, apiKey, secret, token
  • In Network tab, look for any .map files loading
  • The fast way:

    Our Secret Scanner does all of this automatically. It downloads your JavaScript, runs pattern matching for 212+ secret types, checks for exposed source maps, and flags anything suspicious.

    How to Fix It

    Never hardcode secrets. Use environment variables:

    \\\javascript

    // NO. Just no.

    const stripe = require('stripe')('sk_live_abc123');

    // Yes.

    const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

    \\\

    Keep secrets on the backend. If your frontend needs to call Stripe or OpenAI, make it go through your server:

    \\\

    Browser → Your API → Stripe

    (key lives here, never sent to browser)

    \\\

    Disable source maps in production. In webpack:

    \\\javascript

    devtool: process.env.NODE_ENV === 'production' ? false : 'source-map'

    \\\

    Use restricted keys where possible. Stripe publishable keys are meant for the frontend. AWS IAM policies can limit what a key can do. Google Maps keys can be restricted to specific domains.

    If You've Already Leaked a Key

    Don't just generate a new one. The old one is still out there and still valid.

  • Rotate immediately - Create new credentials
  • Revoke the old key - This is the step people forget
  • Check access logs - See if it was already used
  • Check your bills - Look for unexpected charges
  • Scan your git history - Secrets in old commits are still findable
  • And then figure out how it happened and fix your build process so it doesn't happen again.

    Scan your site for exposed secrets Run a security audit