Every application needs credentials: the database password, the key for sending email, a payment service token, a certificate's private key. This data, generally called secrets, is among the most valuable information in an infrastructure. Yet it often ends up in the wrong places: in source code, in a chat message, in a shared file. Managing it well doesn't require expensive tools, but clear rules.
The first mistake: secrets in code
Writing a password directly into code is convenient and dangerous. Code is copied, shared, saved in backups, uploaded to continuous integration services, read by external collaborators. Every copy is a possible leak.
The problem gets worse with Git. If a secret is saved in a commit and then deleted in a later commit, it remains in the history. Anyone with access to the repository can recover it. If the repository is public, there are automated programs that look for keys and tokens as soon as they are published.
Environment variables and .env files
The minimum step is to separate configuration from code, reading secrets from environment variables. The application looks for DATABASE_PASSWORD at startup instead of containing it.
In development it is common to use a .env file loaded automatically. There are two rules:
- the
.envfile must be added to.gitignorebefore you even create it; - the repository holds a
.env.examplewith the variable names and dummy values.
Environment variables do have limits, though. They can appear in error logs, in diagnostic dumps or in the output of commands that list processes. They are an improvement over code, not a complete solution for production.
Secret managers
A secret manager is a service dedicated to safekeeping secrets. All the major cloud providers offer one, and among self-hosted solutions the best known is HashiCorp Vault. Typical features are:
- encryption of secrets at rest and in transit;
- access control: each application reads only the secrets it needs;
- access logging: you know who read which secret and when;
- versions: you can go back to a previous value;
- rotation: some secrets, such as passwords for managed databases, can be changed automatically.
The application authenticates to the secret manager with its own identity, for example the role assigned to the virtual machine or container, and receives the secret at startup. No password is written to disk in plain text.
Comparing the options
| Method | Security | Complexity | Suitable for |
|---|---|---|---|
| Secret in code | Very low | None | Never |
| Unversioned .env file | Low | Minimal | Local development |
| Variables in the hosting or CI platform | Medium | Low | Small projects |
| Encrypted files in the repository | Medium | Medium | Small teams with Git workflows |
| Secret manager | High | Medium | Production |
Principles of good management
Least privilege
Every credential should allow only what is needed. The database user used by the web application doesn't need to drop tables; the key for uploading files to one bucket shouldn't be able to read another. If a credential is stolen, the damage is limited to what it allows.
One secret per use
Don't reuse the same key for development and production, or for two different applications. If one has to be revoked, the others keep working, and the logs show precisely which one was used.
Temporary credentials
Where possible, prefer credentials that expire on their own. Many cloud services let a pipeline or a machine obtain tokens valid for a few minutes, instead of storing permanent keys. A key that expires after an hour is worth little to whoever steals it.
Rotation
Change the most important secrets periodically and, above all, be able to change them quickly. If rotating the database password requires editing ten files on five servers, nobody will do it in the middle of an emergency.
If a secret has ended up in Git
It happens to the best. The order of operations matters:
- Revoke or change the secret immediately with the service it belongs to. This is the most important step: from that moment the leaked copy is worthless.
- Check the service's logs to see whether the credential was used by anyone unauthorized.
- Remove the secret from the code and the history, with tools such as
git filter-repo, knowing that copies already downloaded by others cannot be erased. - Add an automated check to prevent the problem from recurring.
Rewriting history without first revoking the secret is pointless: the key may already have been copied.
Prevention through automation
Tools such as gitleaks or the scanning features built into code hosting platforms analyze every commit looking for keys and tokens. Configured as a pre-commit hook or as a pipeline step, they block the problem before the secret leaves the developer's computer. It is a cheap protection that, in my work, I consider mandatory on every repository.
In short
- Secrets never belong in code: even when deleted, they remain in Git history.
- Environment variables and unversioned
.envfiles are fine for development; production needs a secret manager. - Apply least privilege, use one secret per purpose and prefer temporary credentials.
- If a secret leaks, revoke it first, then check the logs and clean the history.
- Automated scanners on commits and pipelines prevent most incidents.