Secrets
Applies to
practices.no-secrets.
Covers practices.no-secrets.
Two separate places a credential leaks
Most people guard the first and forget the second.
The repository. Once a secret is committed it is in the history forever, and deleting it in a later commit changes nothing - the old commit is still there, still cloneable, still on every machine that ever pulled. The only real remedy is to rotate the credential.
The web root. This one is less obvious and more dangerous. Whatever the deploy filter does not
exclude gets uploaded into a directory a web server serves. An environment file sitting next to
index.php is fetchable over HTTPS by anyone who guesses the filename, and they are all guessable.
A licence key has been served this way from a live site.
So there are two lists to maintain, not one: what git ignores, and what the deploy excludes. They are not the same list and neither implies the other.
What counts
More than passwords:
- Database credentials, for staging as well as production
- SSH hosts, users, keys, and remote paths
- SMTP or transactional mail API keys
- Plugin and theme licence keys
- reCAPTCHA and other service secret keys
- Analytics measurement protocol secrets and conversion API tokens
- Anything in a
.env,.env.local,.env.productionor a backup of one
Host names and remote paths matter too. They are not secrets on their own, but published together they are a map.
The pattern
- Real values live only in an environment file that is git-ignored and deploy-excluded.
- The repository ships an example file with every key present and every value blank or obviously fake, so a new developer knows what to fill in.
- Nothing reads a credential at build time in a way that bakes it into a bundle. A key compiled into JavaScript is public whatever the server does.
- Client-side keys, where a service genuinely requires one, are domain-restricted at the provider.
Verify, do not assume
# Is it in the history at all?
git log --all --full-history -- .env
# Is it being served?
curl -sI https://example.com/.env | head -1 # expect 403 or 404
curl -sI https://example.com/.env.example | head -1
Do not reason about whether a path is served. Request it.
When one does leak
Rotate first. Cleaning the repository is the second step and never the first - the moment a secret is exposed, it should be assumed compromised, and rewriting history does not un-clone anything. Then note it in the handover so nobody spends an afternoon later wondering why the key changed.