API key leakage
Theory
Why This Matters
API key leakage is one of the most frequently reported bug bounty findings and one of the most preventable. In 2022, Toyota's IT partner T-Connect exposed an API key in a public GitHub repository for five years, potentially leaking email addresses of 296,000 customers. In 2019, researchers scanning GitHub found hundreds of AWS access keys, Google API keys, and Stripe secret keys committed in public repositories — many still active. The 2023 CircleCI security incident began with a developer's session token captured from client-side JavaScript. Twilio, Slack, and Uber have all suffered breaches traced to leaked API credentials in source code or version control history. The pattern is consistent: a credential that should be server-side only is exposed in a client-accessible artifact.
Core Concept
An API key is a shared secret that authenticates a client to an API. Unlike OAuth tokens, API keys typically grant long-lived, broad access without per-request signing. An exposed API key is one that has been placed where an attacker can retrieve it without authentication — in JavaScript bundles, HTML source, mobile app binaries, public git repositories, or server error messages.
Exposure vectors by decreasing frequency:
- JavaScript bundles — Frontend build tools (webpack, Vite, Parcel) bundle source files into a single JS file. Any
const API_KEY = "sk-..."orprocess.env.REACT_APP_STRIPE_KEYreference that is included in the bundle is sent to every visitor. Webpack source maps (.js.mapfiles) often expose the original, un-minified source including comments and variable names. - Git history — A key committed and then "deleted" in a later commit still exists in the full git history.
git log -p | grep -i "api.key|secret|token|password"recovers deleted secrets. Services: GitGuardian, truffleHog, gitleaks. .envfiles committed accidentally —.envis excluded from version control by default only if.gitignoreincludes it. A singlegit add .without a proper.gitignorecommits all environment files.- Mobile app decompilation — Android APKs are ZIP files containing
classes.dex(bytecode).apktool d app.apk+jadx -d out/ app.apkreconstruct readable Java/Kotlin. String constants including API keys are preserved in decompiled output. - Error messages and debug endpoints — Stack traces, verbose error responses, and enabled debug endpoints (
/api/debug,/_ah/admin) may include credentials in request logging. - Response headers — Some API gateways include internal auth tokens in
X-Internal-Token,X-Api-Key, orX-Authresponse headers intended for internal routing.
After discovery, the secret rotation workflow is: (1) revoke the key immediately at the provider (Stripe, AWS, Google Cloud console), (2) rotate to a new key deployed via secrets manager (HashiCorp Vault, AWS Secrets Manager), (3) purge the secret from git history using git filter-repo or BFG Repo Cleaner, (4) audit API logs for historical usage by the leaked key, (5) add pre-commit secret scanning to CI.
Technical Deep-Dive
# ── Git history search ─────────────────────────────────────────────────────
# Clone the target repository (or use existing checkout)
git log --all --full-history -p -- "*.env" "*.config" "*.json"
| grep -i "api.key|secret|token|password|sk-|pk-|AKIA"
| sort -u
# Or use truffleHog (high-signal regex + entropy detection)
trufflehog git file:///path/to/repo --only-verified
# Or use gitleaks (fast, many built-in rules)
gitleaks detect --source /path/to/repo --report-format json
# GitHub Dorking for public repos
# (run in browser or via GitHub Search API)
# site:github.com "STRIPE_SECRET_KEY" "sk_live_"
# site:github.com filename:.env "AWS_SECRET_ACCESS_KEY"
# site:github.com "api_key" extension:js "sk-"
# ── Webpack source map extraction ─────────────────────────────────────────
# Check if source maps exist
curl -s https://app.example.com/static/js/main.chunk.js.map |
python3 -c "import sys,json; d=json.load(sys.stdin); [print(s[:200]) for s in d.get('sourcesContent',[]) if 'api' in s.lower()]"
# sourcemapper tool — extracts all original source files from a .map
pip install sourcemapper
sourcemapper -url https://app.example.com/static/js/main.chunk.js.map
-output ./extracted_sources/
# Then grep extracted sources for credentials
grep -rEi "api.?key|secret|token|password|sk[-_]live|AKIA[0-9A-Z]{16}"
./extracted_sources/
# ── Mobile app decompilation ───────────────────────────────────────────────
# Extract and decompile an Android APK
apktool d target.apk -o apktool_out/
jadx -d jadx_out/ target.apk
# Search decompiled source for API keys
grep -rEi "api.?key|secret|sk_live|AKIA[0-9A-Z]{16}" jadx_out/ | head -30
# ── Secret scanning in CI (pre-commit hook) ───────────────────────────────
# .pre-commit-config.yaml
# repos:
# - repo: https://github.com/gitleaks/gitleaks
# rev: v8.18.0
# hooks:
# - id: gitleaks
# ── Verify a discovered key is still active (example: AWS) ────────────────
AWS_ACCESS_KEY_ID=AKIA... AWS_SECRET_ACCESS_KEY=...
aws sts get-caller-identity 2>&1
# If response includes "UserId", key is active → rotate immediately
// Vulnerable pattern: React app with hardcoded API key in source
// This appears in the JavaScript bundle sent to all users
const STRIPE_KEY = "sk_live_AbCdEfGhIjKlMnOpQrStUvWx"; // NEVER do this
const GOOGLE_MAPS_KEY = "AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXX";
// Vulnerable pattern: .env variable included in frontend bundle
// REACT_APP_ prefix causes Create React App to embed in the bundle
const apiKey = process.env.REACT_APP_SECRET_KEY; // Exposed in bundle!
// SECURE: API keys for external services must be used server-side only
// Frontend calls your backend, which calls the external API using the key
// The key is stored in process.env on the server, never sent to the browser
Security Assessment Methodology
- Enumerate JavaScript assets — Spider the target with Burp Suite. For every
.jsURL discovered, check for a corresponding.js.mapsource map. Download all bundles and source maps for offline analysis. - Grep bundles and source maps for credential patterns — Use regex:
(sk|pk)_(live|test)_[A-Za-z0-9]{20,}(Stripe),AKIA[0-9A-Z]{16}(AWS),AIzaSy[A-Za-z0-9_-]{33}(Google),[0-9a-f]{32}(generic),Bearer [A-Za-z0-9._-]+. - Search git history — If source code access is in scope, run truffleHog and gitleaks. Search for
.envfiles at any historical commit. Check all branches, not justmain. - Decompile mobile apps — Download the APK or IPA from the app store listing or directly. Use
jadx(Android) orclass-dump+ Hopper (iOS) to extract strings. - Check HTTP response headers — Capture responses for all authenticated and unauthenticated requests. Look for
X-Api-Key,X-Token,X-Auth,X-Internal-*headers that may carry credentials. - Verify and safely document — For any discovered key, perform the minimum test to confirm validity (e.g.,
aws sts get-caller-identityfor AWS keys). Do not use the key for unauthorized access. Document: key value (truncated in report), discovery location, service, and estimated permission scope. Recommend immediate revocation.
Defensive Countermeasure — All API keys used to call external services must reside exclusively in server-side secrets managers (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager). Never embed secrets in frontend code or include them in environment variables prefixed for frontend bundle injection (
REACT_APP_,VITE_,NEXT_PUBLIC_). Enforce pre-commit secret scanning with gitleaks or GitGuardian. Purge any historically leaked secrets from git history usinggit filter-repo. Implement automatic key rotation schedules and anomaly alerting on API key usage.
Common Assessment Errors
- Only checking the current commit — Secrets deleted from the latest commit persist in git history. Always scan the full history with truffleHog or gitleaks
--log-opts=--all. - Skipping source maps — Minified JS bundles are hard to search;
.js.mapfiles expose the original source with readable variable names and comments. Always check for source maps. - Not verifying key validity before reporting — Reporting an expired or revoked key wastes remediation effort and reduces credibility. Perform a minimal validity check.
- Ignoring mobile apps — Mobile apps are a common location for hardcoded backend API keys. If a mobile app exists for the target, include APK/IPA analysis in the engagement.
- Missing
.env.examplefiles with real credentials — Some developers commit.env.exampleor.env.samplefiles containing actual (not placeholder) credentials. Check these files. - Overlooking error message disclosure — Verbose error responses in staging environments frequently include database connection strings, API keys in headers, or internal service URLs. Test all error-triggering inputs.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0007 | Knowledge of authentication, authorization, and access control methods | Explains API key as a shared-secret authentication mechanism and its exposure failure modes |
| K0065 | Knowledge of policy-based controls for data access | Connects key leakage to secrets management policy failures |
| K0070 | Knowledge of system and application security threats and vulnerabilities | Maps key leakage to real-world breach incidents across major vendors |
| S0001 | Skill in conducting vulnerability scans and recognizing vulnerabilities in security systems | Trains git history search, source map extraction, and mobile decompilation |
| T0028 | Conduct and support authorized penetration testing on enterprise networks | Provides a tool-explicit methodology from discovery through validated reporting |
| T0570 | Conduct application security assessments | Frames API key leakage as a mandatory check in application security assessments |
Further Reading
- truffleHog GitHub Repository — trufflesecurity (offline reference)
- "Finding Secrets in Public GitHub Repos" — GitGuardian State of Secrets Sprawl Report
- OWASP API Security Top 10 2023, API8: Security Misconfiguration — OWASP Foundation
Challenge Lab
Reinforce your learning with a hands-on generated challenge based on this card's competency.