☁ Cloud & DevOps

Secrets management: keeping passwords and keys out of code

Where to keep an app's passwords, tokens and API keys: environment variables, .env files, secret managers, rotation, and what to do if a secret leaks.

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:

  1. the .env file must be added to .gitignore before you even create it;
  2. the repository holds a .env.example with 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:

  1. 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.
  2. Check the service's logs to see whether the credential was used by anyone unauthorized.
  3. 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.
  4. 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 .env files 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.
MM
Marco Marzaduri

Marco Marzaduri works in cybersecurity, digital forensics and web development. With degrees in computer science and psychology, he combines technical analysis with an understanding of the human factor.

Read also