How we manage secrets and credentials
Where secrets live, how we rotate them, and what we never put in a repo, an env file, or a chat thread. The unglamorous habits that keep a client's keys out of the wrong hands.
Most breaches we read about don’t start with a clever exploit. They start with a key in a public repo, a database password pasted into a ticket, or a former contractor whose token still works eighteen months later. Secrets management is boring, and that’s exactly why it fails. This post describes the habits we run on every engagement.
Secrets never live in the repo
Not in the code, not in a committed .env, not in a "temporary" config file we plan to delete. Git history is forever, and a deleted secret is still a leaked secret. The rules are simple:
.envfiles are gitignored from the first commit. We commit a.env.examplewith variable names and fake values so setup is documented without exposing anything.- A secret scanner runs in CI and as a pre-commit hook. A failing scan blocks the merge, not just a warning.
- If a secret does land in history, we treat it as compromised. We rotate it first, then clean up the history. Rewriting git doesn’t un-leak anything.
One home per secret
Every secret has exactly one source of truth: a managed secret store such as the one built into the client’s cloud provider, or a dedicated vault. Applications read from it at deploy or startup time. Nobody keeps a second copy in a notes app, a spreadsheet, or a team chat.
We also keep an inventory. For each secret we record what it unlocks, who owns it, where it’s stored, and when it was last rotated. If we can’t answer "what breaks if this leaks?" in one sentence, we don’t understand the secret well enough to be holding it.
Least privilege, per environment
A credential should be able to do one job and nothing else.
- Separate secrets per environment. Dev, staging, and production never share a key. A leaked dev token must not be able to read production data.
- Scope narrowly. A service that reads from one bucket gets read access to that bucket, not an admin key to the account.
- Prefer short-lived credentials. Where the platform supports it, we use workload identity or temporary tokens instead of long-lived static keys. The best secret is the one that expires on its own.
- Developers don’t get production secrets by default. Access is granted for a reason, for a time, and logged.
Rotation is a practiced routine
A rotation process you’ve never run is a rotation process that doesn’t work. We rotate on a schedule for high-value credentials, and immediately on any of these events:
- Someone with access leaves the team or the engagement.
- A secret appears somewhere it shouldn’t: a log, a screenshot, a support thread.
- A vendor discloses an incident.
We design systems so rotation is a config change, not a code change. That usually means supporting two valid credentials during the cutover, so rotating never requires downtime. If rotating a key would take the system down, we fix that before the key ever needs to be rotated in an emergency.
Humans are the leak path
Technology covers the easy cases. The rest is habit.
- We don’t share credentials over email or chat. When a secret has to move between people, it goes through the secret store or a one-time sharing link that expires.
- Logs get scrubbed. We configure logging and error tracking to redact tokens, passwords, and authorization headers, then test that the redaction works.
- Every person has their own account and their own access. Shared logins make offboarding impossible and audit trails useless.
- We use a password manager and hardware-backed or app-based MFA everywhere it’s offered, especially on the accounts that can mint new secrets: cloud consoles, DNS, source control, and CI.
Offboarding is part of the plan
When an engagement ends, access ends. We agree on the process up front, as part of the handoff: revoke our accounts, rotate every secret we touched, and confirm the client holds the only copies. The client should never wonder whether we can still reach their systems. The answer should be a clear no, and they should be able to verify it.
Why it matters
A leaked credential is one of the cheapest ways for an incident to happen and one of the most expensive to clean up. Good secrets hygiene means a mistake costs a rotation instead of a breach notification, and a departing contractor is a non-event.
For clients, the outcome is straightforward: fewer ways to get hurt, a clear answer when a security questionnaire asks how credentials are handled, and a system their next team can inherit without a treasure hunt. It’s the same discipline we describe in our security practices and Secure SDLC Policy, applied one key at a time.