Group
Secrets2 min read

Environment Variables Are Not Secrets Storage (Part 7)

A pragmatic take on where secrets actually belong, why .env files are a stopgap, and what to graduate to before your first breach.

Every AI-generated app we audit uses .env files as its secrets store. That's fine for a solo developer on a laptop. It stops being fine the moment your app has a second engineer, a CI pipeline, or a production deploy.

Here's why, and what to do about it.

What environment variables are actually good for

Environment variables were designed for configuration: things that change per environment but aren't sensitive. Database host. Log level. Feature flags. Public API URLs.

Somewhere along the way, the industry decided they were also fine for secrets. They're not — they're just marginally better than committing secrets to the repo, and the bar has moved.

Everything that reads your env

Take a moment and count everything that has access to process.env in a running Node app:

  • Your own code
  • Every dependency in your node_modules
  • Every transitive dependency of every dependency
  • Any postinstall script that ran during install
  • Any error reporter that dumps env on crash (some do, by default)
  • The process listing on the box (ps auxe in some configurations)

That is not a secrets store. That is a public bulletin board with a lock on the door.

The .env file specifically

The file itself is a plaintext copy of every secret your app uses, sitting on disk, usually next to the code. Things that go wrong with it:

  1. It gets committed to the repo by mistake (constantly).
  2. It gets copied into a Docker image and pushed to a public registry.
  3. It gets tar'd up with a backup and sent to somewhere less secure.
  4. It gets read by any process on the machine, in plaintext, no auth required.

What to graduate to

You don't need to boil the ocean. Here's a reasonable path:

Stage 1 — you have a .env file

Fine, for now. Do this:

  • Add .env to .gitignore. Verify it's not already in your git history with git log --all --full-history -- .env.
  • Never share .env in Slack. Use a secrets manager for handoff even if you're not using one for runtime.
  • Have a .env.example with keys only, no values committed.

Stage 2 — a real secrets manager

Pick one and use it:

  • AWS Secrets Manager or Parameter Store
  • Google Secret Manager
  • HashiCorp Vault
  • 1Password Secrets Automation
  • Doppler / Infisical if you want a hosted UI

Your app fetches secrets at boot from the manager. The manager handles rotation, audit logs, and access control per-identity.

Stage 3 — no long-lived secrets at all

The end state is that your app doesn't hold API keys — it holds a short-lived identity token (from IAM, OIDC, or a workload identity) and exchanges it for whatever access it needs, scoped tightly, with a TTL measured in minutes.

You don't have to be there tomorrow. But you should know that ".env file" is stage zero, not the finish line.

The rule of thumb

If you can't answer these questions in under thirty seconds, your secrets are not under control:

  • Who has access to production API keys right now?
  • When were they last rotated?
  • If an engineer left the company today, what secrets would you have to rotate?
  • If your .env file leaked to a public GitHub repo, what would you do in the first hour?

Every one of those has an answer. Your job is to make sure the answer isn't "I don't know."

Get an expert audit of your vibe coded app

Don't go live without letting an expert set of eyes review your code. Fortivibe runs 173 checks against your repo, reviewing things like security, stability, and user experience.

Get Your App Audited Now

Team Fortivibe Updated 58 days ago

Copy Link