Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.
SecurityCritical

Leaked Environment Variables

Checks for exposed API keys, tokens, passwords, and other secrets embedded in HTML source, JavaScript bundles, or client-accessible files.

Utilities
Quick take
Typical fix time 20 min
  • Any secret in client-side JavaScript is publicly readable — treat all front-end code as public
  • In Next.js, only variables prefixed with `NEXT_PUBLIC_` are exposed to the browser — never put secrets in these
  • Common leak locations: `.env` files committed to git, hardcoded API keys in JS bundles, service account credentials in `window.__INITIAL_STATE__`
  • Use `git log -S 'keyword'` to search git history for previously committed secrets; rotate any found secrets immediately
  • Tools: GitLeaks, TruffleHog, GitHub Secret Scanning can detect leaks in repositories automatically
Why it matters: An API key embedded in client-side JavaScript gives anyone with a browser devtools tab full access to your cloud services, databases, or third-party APIs — leading to data breaches, unexpected charges, or account takeover.

Rule Details

Any secret placed in client-side JavaScript, HTML, or any file served to browsers is effectively public. Attackers routinely scan public sites and repositories for leaked credentials, and platforms like GitHub secret scanning (opens in new tab) are built around how quickly those exposures get abused.

Code Examples

Framework Environment Variables

# .env.local — SAFE: only available server-side
DATABASE_URL=postgres://user:pass@host/db
STRIPE_SECRET_KEY=sk_live_xxxxx
 
# DANGEROUS: NEXT_PUBLIC_ prefix exposes to browser bundle
NEXT_PUBLIC_STRIPE_SECRET=sk_live_xxxxx  # ❌ Anyone can read this
NEXT_PUBLIC_STRIPE_PUBLISHABLE=pk_live_xxxxx  # ✅ This one IS meant to be public

In Next.js, NEXT_PUBLIC_ variables are inlined into the JavaScript bundle. Only publishable/public keys belong there, which is the same separation described in the OWASP Secrets Management Cheat Sheet (opens in new tab): secrets stay on the server, public identifiers do not.

Hardcoded in Source Code

❌ Secret hardcoded in client code
const apiKey = 'sk_live_abc123xyz'
fetch(`https://api.stripe.com/v1/charges`, {
  headers: { 'Authorization': `Bearer ${apiKey}` }
})
 
✅ Secret stays server-side
// API route (server-side)
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY)

Committed to Git

# Find secrets in git history
git log --all --full-history -- .env
git log -S 'sk_live' --all --oneline
git log -S 'password' --all --oneline
 
# Check current tracked files
git grep -i 'api_key\|secret\|password\|token' -- '*.js' '*.ts' '*.env'

Why It Matters

An API key embedded in client-side JavaScript gives anyone with a browser devtools tab full access to your cloud services, databases, or third-party APIs — leading to data breaches, unexpected charges, or account takeover.

Common Secret Patterns

ServicePattern Example
AWS Access KeyAKIA[0-9A-Z]{16}
AWS Secret40-character alphanumeric
Stripe Live Secretsk_live_[0-9a-zA-Z]{24}
Stripe Publishable (safe to expose)pk_live_[0-9a-zA-Z]{24}
GitHub PATghp_[A-Za-z0-9]{36}
Google API KeyAIza[0-9A-Za-z-_]{35}
JWT tokeneyJ... (base64-encoded JSON header)
Generic bearer tokenBearer [A-Za-z0-9-._~+/]+=*

Architecture: Keep Secrets Server-Side

❌ Direct client → third-party API (with embedded secret key)
 
✅ Client → Your API proxy → Third-party API
   (proxy runs server-side, holds the secret)
// ✅ Server-side API route (Next.js)
// app/api/create-payment/route.ts
export async function POST(request: Request) {
  const { amount } = await request.json()
 
  const paymentIntent = await stripe.paymentIntents.create({
    amount,
    currency: 'usd',
  })
  // STRIPE_SECRET_KEY never leaves the server
  return Response.json({ clientSecret: paymentIntent.client_secret })
}

Preventing Future Leaks

Git Pre-commit Hook with git-secrets

# Install git-secrets
brew install git-secrets  # macOS
 
# Configure for a repository
git secrets --install
git secrets --register-aws  # Add AWS patterns
 
# Add custom patterns
git secrets --add 'sk_live_[0-9a-zA-Z]{24}'
git secrets --add 'ghp_[A-Za-z0-9]{36}'

.gitignore

# Always exclude environment files
.env
.env.local
.env.*.local
.env.production
.env.development
 
# Credential files
*.pem
*.key
credentials.json
service-account.json

If a Secret Is Leaked

  1. Rotate immediately — assume it is compromised the moment it was committed or deployed, because cryptographic failures (opens in new tab) often start with exposed material that teams leave active too long.
  2. Revoke the old credential in the service dashboard
  3. Remove from git history using git filter-repo (preferred) or BFG Repo Cleaner
  4. Notify affected services (AWS, Stripe, etc.) if there is evidence of misuse
  5. Audit access logs for unauthorized usage
Removing from Git History Is Not Enough

Once a secret is pushed to a remote repository — even briefly — it may have been cloned, forked, or cached by the platform. Rotation is mandatory. History rewriting is secondary cleanup.

Exceptions

  • Scanner output, leaked-secret detections, or stack traces should be confirmed as production-relevant before being escalated as blockers.
  • Archived dependencies, sample values, or test fixtures can create false positives, but they should still be documented and bounded clearly.
  • If multiple findings overlap, prioritize the issue that most directly enables compromise or data exposure.

Standards

  • Align the implementation with OWASP: Secrets Management Cheat Sheet and verify the effective response or browser behavior, not only the configuration file.
  • Align the implementation with OWASP Top 10 A02:2021 - Cryptographic Failures and verify the effective response or browser behavior, not only the configuration file.
  • Align the implementation with GitHub: Secret Scanning and verify the effective response or browser behavior, not only the configuration file.

Verification

Automated Checks

  • Test the affected flow in a production-like environment, not just local development.
  • Document any intentional exceptions explicitly.

Manual Checks

  • Inspect the final HTTP response or browser behavior to confirm the control is actually enforced.
  • Verify third-party integrations or embeds still work after the restriction is applied.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Scan the page HTML source and JavaScript bundles for patterns that look like secrets: API keys, tokens, passwords, private keys, connection strings, or credentials. Check for common patterns like sk_, pk_, AIza, ghp_, AKIA, and base64-encoded strings in unusual contexts.

Fix

Auto-fix issues

Move all secrets server-side. Replace client-exposed credentials with server-side API proxies. Rotate any leaked credentials immediately — treat them as compromised. Implement git-secrets or a pre-commit hook to prevent future leaks.

Explain

Learn more

Explain why client-side JavaScript is public code, how secrets leak into bundles, what the impact of a leaked API key is, and how to architect applications to keep secrets server-side.

Review

Code review

Review server config, headers, forms, and integration points related to Leaked Environment Variables. Flag exact responses, cookies, or browser behaviors that violate the rule, and verify them against the effective production-like response.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Mozilla Observatory
observatory.mozilla.orgTool

Rules that often go hand-in-hand with this one.

Protect public forms with CAPTCHA

Public forms that accept user input without authentication must include bot protection to prevent spam, credential stuffing, and automated abuse.

Security
Redirect HTTP to HTTPS

All HTTP requests must be permanently redirected (301) to HTTPS to prevent users from accessing your site over an insecure connection.

Security
Prevent stack trace exposure in production error responses

Production error responses never include stack traces, internal file paths, framework internals, or other debugging detail that could aid an attacker (OWASP A09).

Security
Audit dependencies for known vulnerabilities

Dependencies are regularly scanned for known security vulnerabilities using automated tooling, and critical findings are remediated before deployment.

Security

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385