Skip to main content
CodeRocket
Securitycriticaldata

Leaked Environment Variables

rule · leaked-secrets

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 a new tab) are built around how quickly those exposures get abused.

Code Examples

Framework Environment Variables

Shell
# .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 a new tab): secrets stay on the server, public identifiers do not.

Hardcoded in Source Code

JavaScript
❌ 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

Shell
# 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

Text
❌ Direct client → third-party API (with embedded secret key)
 
✅ Client → Your API proxy → Third-party API
   (proxy runs server-side, holds the secret)
TypeScript
// ✅ 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

Shell
# 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

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 a 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

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.