Skip to content
Security

Secrets Management: Life Beyond the .env File

The real leak vectors ranked, the non-negotiables at any size, a three-rung maturity ladder that mostly stops at rung two, and rotation as a rehearsed muscle with two-key overlap.

5 min read Updated Sep 4, 2026
Secrets Management: Life Beyond the .env File

Every company's secrets management journey has the same first chapter: a .env file, copied over Slack, "temporarily" committed once (it's still in the git history), with a database password that predates half the team. Chapter two is usually written by an incident. This post is about skipping to chapter three — a sane secrets posture proportional to your size — without either the incident or a six-month Vault project nobody asked for.

Secrets flowing from a managed store with rotation and least-privilege access — never through git

The threat model, honestly scoped

What actually leaks secrets, ranked by my incident experience: git history (committed once in 2021 = compromised forever — history is immutable and everyone has a clone), logs and error traces (the connection string helpfully printed in the stack trace, shipped to centralized logging, readable by everyone with dashboard access), over-shared channels (Slack DMs, wikis, onboarding docs), CI configuration (echoed in build logs, exposed to every PR's workflow), and only then the Hollywood scenario of attackers breaching the store itself. Your defenses should follow that ranking — which means hygiene and blast-radius control beat exotic storage tech every time.

The non-negotiables at any size

  • Secrets never enter git. .env in .gitignore from commit #1, an in-repo .env.example with blanks, and — because humans err — gitleaks or trufflehog in CI plus a pre-commit hook. Finding it in CI costs a red build; finding it in history costs a rotation ceremony. And when one does land in history: rotation, not deletion — git filter-repo cleans the embarrassment, but the secret was public the moment it was pushed.
  • Secrets never enter logs. Structured logging with an explicit redaction list (your logger's processor masks password, token, authorization keys), exception handlers that scrub context, and framework debug pages off in production — Laravel's APP_DEBUG=true in prod is a secrets-disclosure endpoint with extra steps.
  • One owner per secret, and an inventory. A boring spreadsheet-grade list: what exists, what it accesses, who owns it, when it last rotated. Unsexy, and the single best predictor of surviving an incident calmly — because "what do we rotate?" answered in minutes versus days is the incident response.

The maturity ladder — climb only as high as you need

Rung 1 — .env done properly (1–5 engineers): env files delivered by your deploy tooling (never by chat), distinct values per environment, production values that developers have never seen (staging credentials for humans, production for machines), and file permissions that mean it (chmod 600, app user only). This rung is legitimate — the sin isn't the file, it's the distribution-by-Slack around it.

Rung 2 — managed secret stores (most teams' correct home): AWS Secrets Manager / SSM Parameter Store, GCP Secret Manager, or on Kubernetes, External Secrets Operator syncing from one of those (plain K8s Secrets are base64-encoded YAML in etcd and git — an encoding, not a protection). What this rung buys: IAM-controlled access (machines fetch secrets at deploy/boot; humans authenticate as themselves, auditably), versioning, and an audit log answering "who read the production DB password and when" — the question chapter-one setups cannot answer at all. CI secrets live in the CI platform's own store, scoped per-environment with protected branches, never echoed (GitLab's masked variables help; discipline helps more).

Rung 3 — Vault-class dynamic secrets (platform-team territory): HashiCorp Vault / OpenBao generating ephemeral credentials on demand — a database login that exists for one hour, per service instance, auto-expiring. Rotation stops being an event because nothing lives long enough to rotate; leaks stop mattering much because the loot expires before it's fenced. Genuinely superb — and an operational commitment (unsealing, HA, policies) that below ~30 engineers usually costs more than it protects. Rung 2 plus discipline covers a shocking fraction of real-world risk.

Rotation: the muscle, not the event

The test of a secrets setup isn't storage — it's "can we rotate everything in an afternoon without downtime?" Because post-incident, that's the task. Requirements: apps that re-read secrets without redeploying (fetch at boot + restart-on-rotate is acceptable; baked-into-image is not), two-key overlap for API credentials (add new key → deploy → verify → revoke old; any provider worth paying supports parallel keys — this is the expand–contract pattern wearing a trench coat), and a calendar: rotate the crown jewels on schedule, not just on breach, so the muscle exists when it's urgent. Least-privilege completes the blast-radius math — the reporting service with read-only DB creds, the notification worker with a send-only API key: when (not if) something leaks, scope is what you'll be grateful for.

The audit-day checklist

□ Scanner in CI; .env.example blank; git history clean (or rotated)
□ Redaction list in the logger; APP_DEBUG off; error pages mute
□ Inventory: every secret has an owner and a last-rotated date
□ Humans never see prod values; machines fetch via IAM, auditably
□ Every credential scoped least-privilege (read-only where read-only)
□ Rotation rehearsed: two-key overlap works, apps reload cleanly
□ CI secrets scoped per environment, masked, unecho-able

Nothing on that list requires new infrastructure until rung 3 — which is the point. Secrets management fails on process, gets blamed on tooling, and is fixed by exactly seven checkboxes of boring discipline. Boring, as ever in security, being the highest compliment available.

Want the checklist run against your actual setup — including the git-history archaeology nobody volunteers for? That's a focused half-day engagement, best scheduled before the incident writes chapter two for you.

Keep reading

Related articles

Security 5 min read

OAuth2 and OIDC in Plain Words

The founding delegation story, the cast renamed into English, the one flow that survived (auth code + PKCE) with every step's attack labeled, ID vs access tokens untangled, and the do-you-even-need-this triage.

Security 4 min read

JWTs vs Sessions: The Debate, Settled-ish

Why revocation is where stateless goes to die, the XSS blast-radius problem, the specific habitats where JWTs genuinely shine, and the sessions-at-the-edge / tokens-between-machines architecture.