A static secret is a credential created once, stored somewhere and reused for months: a database password, an API key, a cloud access token. This lesson is for the DevOps engineer who inherits dozens of these secrets and wants to understand why they are a problem before choosing an alternative. The flaw is not cryptographic: it is the lifetime, the copies and the lack of identity that cost dear.
A measured example shows it. A GitHub ghp_ token committed and then removed in
the next commit is invisible to gitleaks 8.30.1 in dir mode, and found in
git mode in the history, with an exit code of 1. The file is gone, the
secret is not: as long as it is not revoked, it remains usable.
What you will learn
- Define a static secret and its five usage properties
- Explain why lifetime, copying and sharing create the risk
- Recognise six symptoms of immature management in your organisation
- Measure that a secret removed from a file survives in Git history
- Choose a path: vault, dynamic secrets, then workload identity
- Place this approach in requirement SOCLE-SEC-ROT-2 (in French), which favours short-lived identities
What is a static secret
A static secret is a fixed value that exists before it is used, stays valid for a long time and only changes when a human decides to change it. The term is the opposite of the dynamic secret, generated on demand with a short expiry, and of workload identity, which removes the shared secret. The table sums up the five properties that characterise it; each is harmless on its own, it is their combination that creates the risk.
| Characteristic | Description |
|---|---|
| Pre-existence | The secret exists before it is used |
| Persistent storage | It is stored somewhere (file, variable, vault) |
| Frequent sharing | Often reused by several components |
| Long lifetime | Valid for months or years |
| Manual rotation | Changed rarely, often by hand |
Concrete examples
Three forms come up in almost every team. The values below are examples: the AWS ones are the illustration keys published by the official documentation, which give access to nothing.
Database password:
# Defined once in a configuration fileDB_PASSWORD="SuperSecretPassword123!"This password is created at installation, stored in a .env file or a
vault, and reused for the whole life of the application. It is sometimes
shared between several applications that access the same database.
External service API key:
# Generated on the provider's portalSTRIPE_API_KEY="sk_live_abc123xyz789..."This API key is generated once, stored in the CI/CD secrets or the vault, and used by every environment that calls the provider. Many providers offer no alternative to this model.
Cloud access keys:
# AWS access keys created manually (example values from the AWS docs)AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"These access keys are created in the IAM console, stored in the pipeline and used for months. The AWS documentation itself calls them an account security risk and recommends temporary credentials (IAM roles) instead. It also limits a user to two access keys at a time, which caps rotation without downtime.
Why static secrets are a problem
A long, random password remains cryptographically strong for years. It is its usage properties that cause the problem: its lifetime, its tendency to get duplicated and the fact that it carries no identity. The six angles that follow go from the most visible to the most structural.
Lifetime that is too long
The longer a secret exists, the more chances it has to leak. It is discovered by an attacker, copied to an unprotected place, shared with people who no longer need it, or forgotten in an old system. Every month of validity adds a window of exploitation.
The consequence is mechanical: a secret stolen six months ago may still be active today. A token that expires in one hour, by contrast, is worth almost nothing an hour after the theft. The lifetime is therefore the first lever, ahead of even the quality of storage.
Duplication across several layers
A static secret tends to spread. The diagram follows a common path: the value leaves the vault, is copied into a Kubernetes Secret, injected as an environment variable, then leaks into the startup logs or a debug dump. The last two copies are in red: nobody decided on them.
Each copy is an extra attack surface, and above all one more place to clean up on the day of a revocation. The vault protects the original; it protects none of the copies that come from it.
Difficulty of complete revocation
When a static secret is compromised, revocation takes four steps that must all succeed. The first is the hardest, because it requires an up-to-date inventory of the consumers, which most teams do not have.
- Identify every place where the secret is used
- Generate a new secret
- Deploy this new secret everywhere
- Check that the old one no longer works
In practice, the operation is rarely complete. An old script, a forgotten cron job or a copy in a backup keeps using the old value. And revocation depends on the provider: a password changed in a file is not necessarily changed in the database.
A secret removed from the code stays in history
Deleting the line that held the secret does not make it disappear: Git keeps
every version of every file. The following measurement replays the case with
gitleaks 8.30.1, the secrets detector seen in the module on code. A GitHub
ghp_ token is committed in deploy.env, then the file is deleted in the
next commit.
$ gitleaks dir /r --no-bannerINF no leaks found
$ gitleaks git /r --no-banner --redact -vFinding: REDACTEDSecret: REDACTEDRuleID: github-patEntropy: 5.121928File: deploy.envLine: 1Commit: d400395794befbc7039f5fdad842ea1aa9f0f197...INF 1 commits scanned.WRN leaks found: 1$ echo $?1The dir mode only reads the files present and sees nothing; the git
mode walks the history and finds the token, with an exit code of 1 that is
enough to fail a job. Anyone who clones the repository can do the same with
git log -p. The only real protection is revoking the token, covered in the
lesson on the compromised secret.
Poor fit for ephemeral environments
A static secret assumes a stable environment: the application starts once and runs for a long time, the secret is configured at deployment, rotation happens during planned maintenance. This model holds on a server that lives three years.
It breaks when dozens of Kubernetes pods are created per minute, when every serverless function needs access, or when the infrastructure is rebuilt every day. Handing out a fixed value by hand to workloads that live a few minutes amounts to giving each of them a permanent key for temporary use.
Impossible precise attribution
When several services share the same credential, the database logs see only
one user. The diagram shows three services that all connect as app_user: if a
suspicious query appears, nothing says which one sent it. Service C, in
red, can be compromised without the database knowing.
OWASP puts it this way in its Secrets Management Cheat Sheet: services that share the same secrets make identifying the source of a compromise difficult. One credential per service restores attribution, and lets you revoke a single service without cutting off the other two.
Symptoms of poor maturity
The following six situations occur in most organisations that are starting out. Each one is recognised by a configuration snippet and fixed by a precise measure. The values shown are deliberate counter-examples.
Symptom 1: database password shared between applications
The same password is copied into two manifests: on the database side, nothing distinguishes the two applications any more, and compromising one means compromising the other.
# ❌ Application 1env: - name: DB_PASSWORD value: "shared-password-123"
# ❌ Application 2 (same secret)env: - name: DB_PASSWORD value: "shared-password-123"The fix is a distinct credential per application, ideally dynamic: an engine such as Vault or OpenBao can create a database user on demand, with a bounded lifetime.
Symptom 2: admin token used "for convenience"
An admin token ends up in a deployment script because creating a restricted token took a few more minutes. The script then has all the rights of the account that created it.
# ❌ Deployment scriptexport GITHUB_TOKEN="ghp_AdminTokenWithFullAccess..."The fix is a token with minimal permissions, such as a GitHub fine-grained PAT limited to one repository and one duration, read-only if possible. In a pipeline, OIDC even removes this token, as the lesson on secrets in CI/CD shows.
Symptom 3: secret copied between Kubernetes and workstations
The same password lives in two places that have neither the same lifecycle
nor the same level of protection: a cluster object and a .env file on a
workstation.
# The secret is in Kuberneteskubectl get secret db-creds -o jsonpath='{.data.password}' | base64 -d
# ❌ But also in a .env for local developmentcat .envDB_PASSWORD=same-password-hereThe fix is a single source of truth (the vault) and injection at startup of the process, with distinct credentials for local development.
Symptom 4: manual annual rotation
Rotation is treated as a background task, with no binding deadline and no automation. It is therefore systematically postponed by emergencies, and the ticket ends up moving from one quarter to the next.
Ticket: "Annual secrets rotation"Assignee: Ops teamPriority: LowStatus: Postponed (3rd time)A painful rotation will not get done. The fix goes through automated rotation, or better through dynamic secrets that expire on their own and make planned rotation unnecessary.
Symptom 5: old secret never revoked after an incident
The incident did trigger the creation of a new secret, but the old one was never revoked on the provider side. It remains usable, and nobody watches it any more since the incident is closed.
History:- January: suspected leak of the API token- January: new token generated- March: old token still active (forgotten)- June: old token used by an attackerThe fix is a written revocation procedure that ends with a check: try to use the old secret and observe the failure. Without this test, revocation is only an intention.
Symptom 6: same credential in development and production
The same credential goes through all three environments, which aligns the security level of production with that of the least protected development workstation.
# ❌ .env.devDB_PASSWORD="password123"
# ❌ .env.stagingDB_PASSWORD="password123"
# ❌ .env.prodDB_PASSWORD="password123"The fix is a distinct secret per environment, generated automatically, with separate access rights: a developer has no reason to be able to read the production secret.
The real impact: compromise scenarios
The three chains described here are common, not extreme. They share the same tipping point: the damage does not come from the initial theft, it comes from the fact that the stolen secret is still valid when the attacker decides to use it.
Scenario 1: the API key in Git history
A developer commits an API key to test, then removes it in the next commit. As measured above with gitleaks, Git history keeps the value. A year later, an attacker who scans public repositories finds it.
The impact fits in one sentence: the key is still active, because nobody revoked or replaced it. Removing the file gave an impression of a fix without changing anything about the validity of the secret.
Scenario 2: the shared password
Three microservices use the same PostgreSQL password. A vulnerability in the least critical service lets an attacker read the process's environment variables.
The attacker now has access to the database shared by the three services, including the data of the other two. The weakest service sets the security level of the whole database.
Scenario 3: the impossible rotation
A team wants to rotate a critical password, used by five applications in production, two cron jobs, three maintenance scripts and a third-party integration. Coordinating the simultaneous change of eleven consumers seems too risky, and the rotation is postponed.
The secret stays unchanged for three years. It is not a lack of will: it is the coupling between one secret and its consumers that makes rotation dangerous. Dynamic secrets remove this coupling, since each consumer receives its own value.
What to do: the alternatives
No organisation removes its static secrets in one go: some third-party integrations only accept a long-lived key. Progress happens in three stages, first reducing the risk carried by existing secrets before changing model. The following lessons of this module detail each stage.
Short term: manage static secrets better
Even without being able to remove static secrets, five steps greatly reduce the risk. The first, centralisation, conditions the other four: you cannot trace or rotate a secret whose location you do not know.
- Centralise in a vault (OpenBao, Vault, Infisical)
- Separate by environment (development, staging, production)
- Limit permissions to the strict minimum
- Trace all access
- Plan regular rotations, automated if possible
Medium term: adopt dynamic secrets
The most critical secrets can become dynamic: generated on demand, specific to each consumer and expired automatically. Three forms are common: database credentials created by a secrets engine, short-lived cloud tokens, and automatically renewed certificates.
The gain is twofold. Rotation disappears as a task, since each value expires on its own, and attribution comes back, since each consumer has its own value. The lesson on the secret lifecycle shows how the TTL (time to live) replaces planned rotation.
Long term: workload identity
For machine-to-machine exchanges, the next stage removes the shared secret in favour of a workload identity (the identity of a running program). Three families exist: SPIFFE/SPIRE for internal cryptographic identities, the cloud providers' OIDC federation (GCP, AWS, Azure) for access to cloud APIs, and mTLS between services.
This step raises a prior question, that of secret zero: who gives the application the first secret that opens the vault? The two lessons that follow the one on the lifecycle answer it, and show why attestation by the platform breaks the loop.
Assessment checklist
These six questions measure your current maturity. An answer far from the right-hand column points to the symptom to tackle first; tested revocation is the one teams tick least often.
| Question | Ideal answer |
|---|---|
| How long do your secrets stay valid? | Hours or days, not months |
| Is a secret shared between several services? | No, one per service |
| Can you rotate a secret in less than an hour? | Yes, in an automated way |
| Do you know which services use each secret? | Yes, with certainty |
| Do environments have distinct secrets? | Yes, always |
| Is secret revocation tested regularly? | Yes, in drills |
Key points
- A static secret is cryptographically strong; its lifetime, its copies and its sharing are what create the risk.
- Each copy (Kubernetes Secret, environment variable, logs, dump) is one more place to clean up on the day of a revocation.
- A secret removed from the code stays in Git history: gitleaks 8.30.1 finds it in
gitmode, not indirmode. - A credential shared between services makes attribution impossible and prevents revoking a single consumer.
- A manual, painful rotation will not get done: automation or dynamic secrets.
- The path goes from the vault to dynamic secrets, then to workload identity.
- The evidence expected by SOCLE-SEC-ROT-2 is the use of short-lived or ephemeral identities (OIDC) favoured over long-lived secrets.
Knowledge check
Check that you have the essentials of this lesson. The questions only cover what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Secret lifecycle: the 8 key stages
Continue the course: Secret lifecycle: the 8 key stages
A new full quiz with random questions
Work again only on the questions you missed