Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
Academy API Key Exposure
Beginner · 15 min

API Key Exposure

Learn how API keys leak through JavaScript bundles, git history, and query strings, and how to keep them server-side.

1 Keys in JS Bundles, Git History, and Query Strings

API keys embedded in client-side JavaScript bundles are visible to anyone who views your site's source code. This is an extremely common mistake with payment, analytics, and third-party API keys.

Common exposure vectors:

// In frontend JavaScript (bundled and served to all users!)
const STRIPE_SECRET = "sk_live_AbCdEfGhIjKlMnOpQrStUvWx";
const response = await fetch("/api/charge", {
  headers: { "X-API-Key": "api_key_real_value" }
});

Query string exposure:

GET /api/data?api_key=secret_value HTTP/1.1
# Leaks in server logs, browser history, Referer headers, analytics

Git history:

# Even after deletion, keys remain in git history
git log --all -p | grep "sk_live_"  # Key found in old commit!

Automated scanners like GitGuardian, truffleHog, and GitHub's secret scanning find and alert on exposed keys within minutes of exposure.

2 Backend Proxy and Environment Variables

Secret API keys must never reach the client. Use a backend proxy to make API calls server-side, where the key stays private.

Backend proxy pattern:

// BAD: frontend calls Stripe directly with secret key
// sk_live_key is in browser bundle!

// GOOD: frontend calls YOUR backend, which calls Stripe
// Frontend (React):
const result = await fetch("/api/create-payment", {
  method: "POST",
  body: JSON.stringify({ amount, currency })
});

// Backend (Node.js) — sk_live_key only lives here
app.post("/api/create-payment", authenticate, async (req, res) => {
  const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
  const intent = await stripe.paymentIntents.create({
    amount: req.body.amount,
    currency: req.body.currency
  });
  res.json({ clientSecret: intent.client_secret });
});

For truly public keys (Stripe publishable key, Google Maps public key), use the restricted/publishable version — not the secret version.

Defense checklist:

  • Never include secret API keys in frontend JavaScript
  • Use environment variables on the backend (never hard-coded)
  • Use backend proxy routes for third-party API calls
  • Use restricted/scoped keys with minimum required permissions
  • Enable secret scanning in your git repository

Knowledge Check

0/3 correct
Q1

Why are API keys in frontend JavaScript bundles dangerous?

Q2

What is the correct architecture for using a secret API key (like Stripe secret key) from a web app?

Q3

Why are API keys in URL query strings particularly risky?

Code Exercise

Move API Key to Backend

The frontend makes API calls with a secret key embedded in the code. Refactor the backend endpoint to proxy the API call server-side, and update the frontend to call the backend instead.

javascript