A hardcoded Stripe secret key (sk_live_*) in source code becomes a permanent fixture of your git history. Even if you delete the line in a later commit, the key is recoverable by anyone with read access to the repository — including all historical collaborators, any connected CI/CD system, and anyone who ever cloned the repo. CWE-798 (Use of Hard-coded Credentials) and PCI-DSS 4.0 Req 3.6 (protection of cryptographic keys) both treat this as a critical violation. OWASP A07 (Identification and Authentication Failures) flags hardcoded credentials as a root-cause pattern for account takeover. The only safe recovery from a hardcoded secret is immediate key rotation — the exposed key must be treated as permanently compromised.
High because a hardcoded key is permanently embedded in git history, meaning rotation is required the moment it is detected and any delay leaves the Stripe account accessible to repository readers.
Load all payment credentials exclusively from environment variables and validate their presence at startup.
// lib/stripe.ts
import Stripe from 'stripe'
if (!process.env.STRIPE_SECRET_KEY) {
throw new Error('STRIPE_SECRET_KEY is not set')
}
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2024-12-18.acacia',
})
Check .env.example into version control with placeholder values only:
STRIPE_SECRET_KEY=sk_test_replace_me
STRIPE_PUBLISHABLE_KEY=pk_test_replace_me
STRIPE_WEBHOOK_SECRET=whsec_replace_me
If any key was ever hardcoded and committed, rotate it in the Stripe Dashboard immediately — git history is not erasable without a full rewrite.
ID: ecommerce-payment-security.payment-integration.keys-from-env
Severity: high
What to look for: Count every source file that references payment provider key patterns (sk_live_, sk_test_, pk_live_, pk_test_, whsec_, rk_live_). For each match, classify it as environment-loaded (process.env.*) or hardcoded (string literal). Search config files, service initializers, utility modules, JSON config files, database seed scripts, and test fixtures. Verify that .env or .env.local is listed in .gitignore.
Before evaluating: Quote the variable name and assignment pattern (not the actual secret value) for each key reference found. Example: "Found const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!) in lib/stripe.ts line 5."
Pass criteria: All payment API keys and signing secrets are loaded exclusively via process.env.* (or framework equivalent). No more than 0 hardcoded key literals should exist. No key material appears as a string literal anywhere in source files. .env files are gitignored.
Fail criteria: Any payment key is hardcoded as a string literal in any source file, regardless of whether that file is client-side or server-side.
Skip (N/A) when: The project uses a managed payment service where credentials are injected by the platform (e.g., Shopify Payments built-in), and no manual key configuration exists.
Detail on fail: Name the specific file and key type. Example: "Stripe secret key hardcoded as string literal in lib/stripe.ts — rotate this key immediately as it may have been committed to version control"
Cross-reference: The Environment Security audit covers comprehensive credential scanning and .env hygiene across all file types, not limited to payment keys.
Remediation: Load all credentials exclusively from environment variables:
// lib/stripe.ts — CORRECT
import Stripe from 'stripe'
if (!process.env.STRIPE_SECRET_KEY) {
throw new Error('STRIPE_SECRET_KEY environment variable is not set')
}
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
apiVersion: '2024-12-18.acacia',
})
Add the following to .env.example (checked into version control with no actual values):
STRIPE_SECRET_KEY=sk_test_your_key_here
STRIPE_PUBLISHABLE_KEY=pk_test_your_key_here
STRIPE_WEBHOOK_SECRET=whsec_your_secret_here
If a key was ever hardcoded and committed, rotate it immediately in your provider's dashboard — git history is permanent.