Every application has secrets: API keys, database passwords, tokens, certificates. They're the keys to your kingdom — and they're routinely mishandled. Hard-coded in source, committed to git, pasted into chat, left in plain config files. Secrets management is the practice of handling them safely, and it's one of the highest-leverage security improvements a team can make.
What counts as a secret
A secret is any credential that grants access to something:
| Secret | Grants access to |
|---|---|
| API keys | Third-party services (payments, email, cloud) |
| Database passwords | Your data |
| Tokens / JWT signing keys | Authentication |
| TLS private keys | Your HTTPS identity |
| Cloud credentials | Your entire infrastructure |
Leak any of these and the consequences range from "embarrassing" to "company-ending."
The mistakes everyone makes
Secrets leak in depressingly predictable ways:
- Hard-coded in source code — and then committed to git, where they live in history forever even after you delete them.
- Committed to a public repo — bots scan public repos for keys within minutes.
- Shared in chat or email — now in a dozen inboxes and message histories.
- In plaintext config files — readable by anyone who reaches the server.
- Never rotated — a key from three years ago, shared with people long gone.
The number-one rule: never put a secret in your code. Once a secret is committed to git, assume it's compromised — it's in the history, possibly on a fork, possibly scraped by a bot. Removing it later isn't enough; you have to rotate it.
How to do it right
The core idea: keep secrets out of code, store them securely, and inject them at runtime.
| Practice | What it does |
|---|---|
| Environment variables | Keep secrets out of code (good baseline) |
| A secrets manager / vault | Encrypted storage with access control + auditing |
| Inject at runtime | The app fetches secrets when it runs, not from source |
| Least privilege | Each secret grants the minimum access needed |
| Rotation | Change secrets regularly and after any suspected leak |
| Audit logging | Know who accessed which secret, and when |
A dedicated secrets manager (a "vault") is the gold standard: secrets are encrypted at rest, access is controlled and logged, and you can rotate them centrally. Code asks the vault for a secret at runtime — the secret never lives in the repo.
If a secret leaks
Speed matters. If a key is exposed:
- Rotate it immediately — generate a new one and revoke the old.
- Assume it was used — check logs for unexpected access.
- Find the root cause — how did it leak? Fix that (e.g. add a pre-commit secret scanner).
Don't just delete the commit — the secret is already out. Rotation is the only real fix.
This is part of Zero Trust
Tight secret handling — least privilege, short-lived credentials, auditing — is a core piece of Zero Trust. The less each credential can do and the faster it rotates, the less damage any single leak causes.
The bottom line
| In one line | |
|---|---|
| What | Safely storing and accessing credentials. |
| Never | Hard-code secrets or commit them to git. |
| Do | Use a vault, inject at runtime, least privilege, rotate. |
| If leaked | Rotate immediately — deleting the commit isn't enough. |
Secrets are the master keys to everything you've built, yet they're treated more carelessly than almost anything else in software. Keep them out of code, store them in a vault, grant the least access, and rotate often — and a single leak stops being a catastrophe.
Related: Zero Trust, what is a CVE?, mTLS explained.