# Separate environment configs for dev/staging/prod

- **Pattern:** `ab-001262` (`environment-security.secrets-management.env-separation`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001262
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001262)

## Why it matters

Using the same environment configuration across development, staging, and production means a developer mistake in a test environment can hit production data, a staging webhook fires against live Stripe, or a misconfigured query runs against the production database. CWE-1188 (Use of Hardcoded Default Values) and OWASP A05 both flag shared configurations as a misconfiguration risk. NIST CM-6 requires controlled configuration management — shared `.env` files with no environment separation make that impossible to enforce. Beyond security, shared configs make incident response harder: you cannot revoke a staging credential without also revoking the production one.

## Severity rationale

High because shared environment configs mean a misconfiguration, test, or breach in any environment can directly impact production systems and data.

## Remediation

Create distinct configuration files for each environment and load them based on `NODE_ENV`. Each file must use environment-specific credentials, endpoints, and API keys — never the same values.

```
.env.development   # local dev: localhost DB, Stripe test keys, sandbox API endpoints
.env.staging       # staging server: staging DB, Stripe test keys, staging endpoints
.env.production    # production server: production DB, Stripe live keys, production endpoints
```

```ts
// At server startup
import dotenv from 'dotenv'
dotenv.config({ path: `.env.${process.env.NODE_ENV ?? 'development'}` })
```

All three files go in `.gitignore`. Maintain a `.env.example` with placeholder values for each variable so new developers know what to fill in.

## Detection

- **ID:** `env-separation`
- **Severity:** `high`
- **What to look for:** Check for presence of environment-specific configuration files or environment variable patterns. Look for `.env.development`, `.env.staging`, `.env.production`, or similar. Check that each environment has distinct API endpoints, database connections, and API keys.
- **Pass criteria:** Count all environment-specific config files found. Each environment (development, staging, production) must have its own configuration -- at least 2 distinct environment configs must exist. Databases, API keys, and endpoints differ per environment.
- **Fail criteria:** All environments share the same .env file or configuration, or no environment separation is evident.
- **Skip (N/A) when:** The project is single-environment only (e.g., local development only, no staging or production).
- **Detail on fail:** `"Only .env file found; no .env.development, .env.staging, .env.production separation"` or `"All environments point to the same database and API endpoints"`.
- **Remediation:** Create environment-specific config files:

  ```
  .env.development   # local dev, localhost, test keys
  .env.staging       # staging server, staging API endpoints, staging DB
  .env.production    # production server, production endpoints, production DB
  ```

  Load the appropriate file based on `NODE_ENV`:
  ```ts
  import dotenv from 'dotenv'
  dotenv.config({ path: `.env.${process.env.NODE_ENV}` })
  ```

## External references

- cwe CWE-1188
- owasp A05
- nist CM-6

Taxons: operational-readiness, cryptography-and-secrets

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