# Secrets managed via environment variables or secrets manager; no credentials committed to version control

- **Pattern:** `ab-000989` (`deployment-readiness.environment-configuration.secrets-management`)
- **Severity:** info
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000989
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000989)

## Why it matters

Hardcoded credentials in source code (CWE-798) or committed `.env` files (CWE-312) expose API keys, database passwords, and service tokens to every person with repository access — and to public GitHub if the repo is ever made public or forked. OWASP A07 (Identification and Authentication Failures) and NIST IA-5 both classify hardcoded credentials as a critical authentication control failure. PCI DSS v4.0 Req-8.6.2 explicitly prohibits hardcoded passwords/passphrases in scripts, configuration/property files, and bespoke source code. Unlike most vulnerabilities, secrets in git history persist even after the secret is removed from the current codebase — they require key rotation, not just file deletion.

## Severity rationale

Info in this bundle context because other patterns cover active secret exposure; this pattern focuses on the secrets management hygiene baseline rather than a detected active leak.

## Remediation

Store all secrets in environment variables and exclude `.env` files from version control. Create `.env.example` as a committed template with placeholder values.

```bash
# .env.example — commit this; never commit .env
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
STRIPE_SECRET_KEY=sk_test_REPLACE_ME
SENTRY_DSN=https://REPLACE_ME@sentry.io/123
```

Add to `.gitignore`:

```
.env
.env.local
.env.*.local
```

If secrets were ever committed, rotate them immediately — do not rely on `git rm` alone, as the values remain in history. Use `git filter-repo` to scrub history, then force-push. For production secret management: Vercel Dashboard → Settings → Environment Variables; AWS Systems Manager Parameter Store; or HashiCorp Vault for self-hosted.

## Detection

- **ID:** `secrets-management`
- **Severity:** `info`
- **What to look for:** Enumerate every relevant item. Check for `.env.example` or similar template file (without actual values). Look for secrets management in code: environment variable usage, AWS Secrets Manager integration, HashiCorp Vault, or similar. Verify `.gitignore` excludes `.env`, `.env.local`, or similar files. Check git history for any committed secrets.
- **Pass criteria:** At least 1 of the following conditions is met. Secrets are stored in environment variables or a secrets manager. No credentials are committed to version control. A `.env.example` exists as a template.
- **Fail criteria:** Secrets are hardcoded in source code or config files, or committed to git history. No `.env.example` template.
- **Skip (N/A) when:** The project has no secrets or external service dependencies.
- **Detail on fail:** `"API keys hardcoded in config files. No .env.example template found."` or `"AWS credentials committed to git history (possibly already rotated)."`
- **Remediation:** Use environment variables. Create `.env.example`:

  ```bash
  # .env.example (commit this to git)
  DATABASE_URL=postgres://user:pass@localhost:5432/db
  API_KEY=your_api_key_here
  SENTRY_DSN=https://xxx@sentry.io/123
  ```

  Then add to `.gitignore`:

  ```
  .env
  .env.local
  .env.*.local
  ```

  In your app, use `process.env`:

  ```ts
  const apiKey = process.env.API_KEY;  // Never commit the real value
  ```

  Use your platform's secrets manager for deployment:
  - Vercel: Dashboard → Settings → Environment Variables
  - AWS: Systems Manager → Parameter Store or Secrets Manager
  - Railway/Render: Add env vars in UI before deploying

## External references

- cwe CWE-798
- cwe CWE-312
- owasp A07
- nist IA-5
- pci-dss Req-8.6.2

Taxons: cryptography-and-secrets

HTML version: https://auditbuffet.com/patterns/ab-000989
