Secrets management is the practice of storing, distributing, rotating and revoking credentials such as API keys, database passwords, tokens and certificates, so they never sit in plain text in code, config files or CI pipelines. The goal is simple to state: applications fetch credentials at runtime from a controlled store, most credentials are short-lived, cloud access from CI/CD uses federated identity instead of stored keys, and scanning catches anything that slips through. When a secret does leak, you revoke it first and clean up afterward.

What counts as a secret?

A secret is any value that grants access or proves identity. Common examples:

  • API keys and access tokens for SaaS and cloud services
  • Database usernames and passwords
  • Cloud provider access keys
  • Private keys for TLS certificates, code signing and SSH
  • Webhook signing secrets and encryption keys
  • Service account credentials and personal access tokens

Source code isn't the only place these end up. They turn up in .env files, container images, infrastructure-as-code templates, CI variables, build logs, wikis, tickets and chat threads.

Why hard-coded credentials are a problem

Hard-coding credentials is common enough to have its own entry in the Common Weakness Enumeration, CWE-798: Use of Hard-coded Credentials. The risks are practical rather than theoretical:

  • Code travels. Repositories get cloned to laptops, forked, mirrored, shared with contractors and occasionally made public by mistake. Every copy carries the secret.
  • History is permanent. Deleting a secret in a later commit doesn't remove it from the repository's history.
  • Secrets outlive their purpose. A key committed years ago may still work, and nobody remembers what it unlocks.
  • Rotation becomes painful. Changing a hard-coded value means a code change and a redeploy, so it rarely happens.
  • Access is too broad. Anyone with read access to the repo effectively has the credential too.

What does a secrets vault do?

A secrets vault, or secrets manager, is a central service that stores credentials encrypted and hands them to authorized workloads at runtime. Options include self-hosted open-source software and the native secrets services offered by cloud providers. Whichever you choose, look for these capabilities:

  • Encryption at rest and in transit, ideally backed by a hardware security module or a cloud key management service
  • Fine-grained access policies, so each application can read only its own secrets
  • Strong authentication for workloads, using platform identity rather than yet another static password
  • Audit logging of every read, write and policy change
  • Versioning and rotation support, so you can roll a secret forward and back
  • Dynamic secret generation for databases and cloud services, covered below

Getting secrets into applications

The application should fetch the secret at startup or on demand, using its own workload identity to authenticate to the vault. Common patterns include an SDK call in the application, a sidecar or agent that writes secrets to a memory-backed file, or a platform integration that injects them at deploy time.

Be careful with environment variables. They're convenient, but they can leak through crash dumps, debug pages, process listings and child processes. If you use them, keep them out of logs and error output. On Kubernetes, remember that native Secrets are base64-encoded rather than encrypted by default, so enable encryption at rest for the cluster's data store and restrict who can read Secret objects.

Why use dynamic and short-lived secrets?

A static secret works until someone changes it, which might be never. A dynamic secret is created on demand for a specific workload and expires automatically.

For example, instead of every instance of an application sharing one long-lived database password, the vault creates a unique database user for each instance with a lifetime of an hour or so. When the lease expires, the vault deletes the user. If a credential is exposed, it's only useful briefly, and the audit log shows exactly which workload it belonged to.

The same principle applies more broadly. Prefer short-lived tokens over long-lived ones, prefer credentials scoped to one task over shared ones, and prefer credentials that expire on their own over ones that depend on someone remembering to rotate them.

Use workload identity and OIDC federation for CI/CD

CI/CD pipelines are one of the most common homes for long-lived cloud credentials. A deployment job needs to reach a cloud account, so someone creates an access key, pastes it into the pipeline's secret variables and forgets about it. That key often has broad permissions and never expires.

Workload identity federation removes the stored key entirely. Most major CI/CD platforms can issue an OpenID Connect (OIDC) token to each job, and the major cloud providers can accept those tokens in exchange for short-lived credentials.

How OIDC federation works

  1. You register the CI platform as a trusted identity provider in your cloud account.
  2. You create a role and a trust policy that says which pipelines may assume it.
  3. When a job runs, the CI platform issues a signed OIDC token with claims describing the job, such as the repository, branch and environment.
  4. The job presents the token to the cloud provider, which checks the signature and claims against the trust policy.
  5. If they match, the cloud provider returns temporary credentials that expire shortly after the job finishes.

There's nothing to store, nothing to rotate and nothing useful to leak from the pipeline configuration.

Scope the trust policy tightly

Federation is only as good as its trust conditions. A policy that trusts any repository in your organization, or worse, any repository on the platform, hands cloud access to far more code than you intended. Restrict the trust policy to specific repositories, and for production roles, to specific branches or protected deployment environments. Give each role only the permissions that pipeline needs.

The same idea applies outside CI/CD. Workloads running in the cloud should use the platform's built-in identity for their compute service rather than stored keys. Across mixed environments, the open-source SPIFFE standard and its reference implementation, SPIRE, provide a vendor-neutral way to issue short-lived identities to workloads.

How secret scanning catches what slips through

Even with a vault and federation in place, people will occasionally paste a key into code. Scanning is the safety net, and it works best in layers.

Layer When it runs What it catches
Pre-commit hooks On the developer's machine, before a commit is created Secrets before they ever enter history
Push and CI scanning When code is pushed or a pipeline runs Anything that bypassed local hooks
Repository scanning Continuously, across all repositories New secrets and newly recognized secret formats
History scanning Once for each repository, then periodically Old secrets buried in earlier commits
Artifact scanning On container images, packages and build logs Secrets baked into build outputs

Open-source tools such as Gitleaks and detect-secrets can run as pre-commit hooks and in CI, and many code hosting platforms offer built-in secret scanning and push protection. Pre-commit hooks are optional on a developer's machine, so treat them as a convenience and rely on server-side checks for enforcement.

Run a full history scan when you first set this up. Expect findings. Triage them by asking whether each secret is still valid, and revoke the live ones before doing anything else.

How often should you rotate secrets?

Rotation limits how long a stolen secret stays useful. Base the schedule on risk:

  • Dynamic and federated credentials rotate themselves by expiring. That's the goal for as many secrets as possible.
  • High-value static secrets, such as production database credentials or signing keys, should rotate on a fixed schedule and immediately after any suspected exposure.
  • All static secrets should rotate when someone who had access to them leaves or changes roles.

Automate rotation wherever you can. Manual rotation gets skipped when teams are busy. To avoid downtime, use a two-secret overlap: create the new credential, deploy it, confirm everything uses it, then revoke the old one. Many services support two active keys at once for exactly this reason.

What to do when a secret leaks

Treat any secret that has been committed, pushed, logged or pasted somewhere shared as compromised, even if the repository is private. Then work in this order:

  1. Revoke or rotate the secret immediately. This is the step that actually stops misuse. Don't wait until you've cleaned the repository or finished investigating.
  2. Deploy the replacement to the systems that legitimately use it, ideally from your vault.
  3. Check for misuse. Review the provider's access logs for activity using the leaked credential from the time it was exposed until it was revoked.
  4. Work out the scope. What could the secret access? Were other secrets reachable from there?
  5. Clean up the source. Remove the secret from the code and, if appropriate, rewrite history. Remember that forks, clones, caches and CI logs may still hold copies, which is why revocation comes first.
  6. Fix the cause. Move the credential into the vault or replace it with federation, and add or tune scanning so the same pattern gets caught next time.

Write this down as a short runbook, with contacts for each major provider and instructions for revoking each type of key. During an incident, nobody should be searching for how to disable an API key.

Frequently asked questions

Are environment variables a secure way to store secrets?

They're better than hard-coding, but they're a delivery mechanism, not storage. The secret still has to come from somewhere, and environment variables can leak through logs, crash reports and child processes. Load them from a vault at runtime and keep them out of output.

Is a private repository safe for secrets?

No. Private repositories are cloned to many machines, shared with integrations and sometimes made public by mistake. Anyone with read access has the secret, and it stays in history even after it's deleted.

Do we need a vault if we use OIDC federation?

Usually yes. Federation removes cloud access keys from pipelines, but applications still need database passwords, third-party API keys and signing keys. A vault handles those, ideally generating dynamic credentials where the target system supports it.

Next steps

  1. Run a history scan across your repositories and revoke any live secrets it finds.
  2. Add secret scanning to CI and turn on push protection where your platform offers it.
  3. Replace stored cloud keys in CI/CD with OIDC federation, scoped to specific repositories and branches.
  4. Move remaining application secrets into a vault with per-application access policies.
  5. Switch high-value database credentials to dynamic secrets.
  6. Write a leaked-secret runbook that starts with revocation.