Most breaches don't start with a clever exploit. They start with a credential: an AWS key pushed to a public repository, a database password in a CI log, a token in a Slack message. Secrets are the keys machines use to talk to each other, and they are the classic stepping-stone asset. Rarely what an attacker wants, almost always how they get it.
What counts as a secret
- Cloud access keys and service account keys
- Database and message broker passwords
- Third-party API keys (payment, email, AI providers)
- OAuth client secrets and refresh tokens
- Webhook signing secrets
- TLS and SSH private keys, code-signing keys
- Encryption keys and key-encryption keys
Where secrets leak
| Leak path | How it happens |
|---|---|
| Source code | Hard-coded keys and committed .env files, found by bots within minutes on public repositories, and by anyone with read access internally (CWE-798) |
| CI/CD | Secrets printed in build logs, exposed to pull requests from forks, or available to every job instead of just the deploy step |
| Container images | Secrets copied into image layers during the build, still present even if a later layer deletes them |
| Runtime | Environment variables shown on debug pages, crash reports and process listings |
| Collaboration tools | Keys pasted into chat, tickets and wikis "just for a minute" |
| Client applications | API keys embedded in mobile apps or JavaScript bundles, which anyone can extract |
| Third parties | Your keys stored by a vendor or integration that is itself breached |
Principles of good secrets management
- Eliminate static secrets where you can. Workload identity (cloud IAM roles for compute, Kubernetes service account federation) and OIDC federation from CI to the cloud issue short-lived credentials automatically, so there is no long-lived key to leak.
- Centralise the rest in a secrets manager or vault (cloud-native services such as AWS Secrets Manager, Azure Key Vault and Google Secret Manager, or self-hosted ones such as HashiCorp Vault and OpenBao), never in code or images.
- Scope tightly. One secret per service per environment, with the minimum permissions (least privilege). A shared "integration key" used by five services is five times the blast radius.
- Rotate automatically. Short lifetimes limit how long a leak is useful, and regular rotation proves you can do it in an emergency.
- Audit access. Log which workload read which secret, and alert on unusual use.
- Scan for leaks. Secret scanning in pre-commit hooks, CI and repository hosting catches most accidental commits.
- Have a revocation runbook. For each critical secret: who owns it, how to revoke it, what breaks when you do, and how to check whether it was used.
Worked example: Invoicely
Invoicely holds four critical secrets: the database password, the Stripe API key, the Stripe webhook signing secret and the email provider's API key. An attack tree for "send fraudulent invoices from Invoicely's domain" shows the email key as the target:
- Leak paths (OR): committed in an old
.envfile; printed in a CI deploy log; readable by the PDF worker, which doesn't need it. - Impact: phishing sent from Invoicely's real domain, passing email authentication checks, to every customer's clients.
Mitigations: move all four secrets into the cloud provider's secrets manager; give the PDF worker access only to file storage; use CI-to-cloud OIDC federation so the pipeline has no static key; add secret scanning to the repository; and write the revocation steps for each key into the incident runbook. The Stripe webhook secret also closes threat T4, but only if the API actually verifies the signature.
Secrets in a threat model
Secrets are usually invisible on architecture diagrams, which is why they get missed. On a DFD:
- Add the secrets manager and the CI/CD pipeline as elements.
- Label each flow with the credential it uses.
- For each process, list every secret it can read, and ask what an attacker could do with each one if that process were compromised.
The pipeline deserves special attention: it often holds the most powerful credentials in the company. Our guide to threat modeling a CI/CD pipeline covers it in depth.
Common mistakes
- Deleting the commit instead of rotating the key. Once a secret has been pushed, assume it has been copied.
- A vault with one master token in an environment variable. The secret-zero problem: solve it with workload identity, not another static key.
- Secrets without owners. Nobody rotates a key nobody owns, and nobody knows what breaks if it's revoked.
Key takeaways
- Secrets are stepping-stone assets: rarely the goal, almost always the route to it.
- The most common leaks are source code, CI logs, container images, chat messages and over-shared environment variables.
- The best secret is one that doesn't exist: prefer short-lived, automatically issued credentials (workload identity, OIDC federation) over static keys.
- Plan for leaks. Every secret needs an owner, a scope, a rotation process and a way to revoke it within minutes.
Frequently asked questions
What counts as a secret?
Any value that grants access or proves identity to a machine: API keys, database passwords, OAuth client secrets and refresh tokens, cloud access keys, SSH and TLS private keys, signing keys, encryption keys and webhook signing secrets.
Are environment variables a secure way to store secrets?
They are better than hard-coding, but not a complete answer. Environment variables are visible to every process in the container, often end up in crash dumps, debug pages and logs, and are usually long-lived. A secrets manager that injects short-lived values, or workload identity that avoids static secrets, is safer.
What is secret rotation?
Replacing a secret with a new one on a schedule or after a suspected leak, and revoking the old one. Automated rotation reduces how long a leaked secret stays useful, and proves you can replace it quickly in an incident.
What should I do if a secret is committed to Git?
Revoke and replace it immediately; that is the only fix. Removing the commit is not enough, because forks, clones, CI caches and secret-scanning bots may already have a copy. Then check the provider's logs for use of the leaked secret.