Skip to main content

Free 30-min security demo Book Now

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

Put it into practice

Find API Key Exposure in your own code

Offensive360 SAST traces the tainted data flow behind this class of bug across 60+ languages and ships a secure fix with every finding. Public open-source projects scan free; teams can see it on their own repository in a 30-minute demo.