A credential is exposed. Not necessarily in Git — in a log, a screenshot, a chat message, a support bundle, a container image, or a repository somebody made public by accident.
The response is the same regardless of how it got out, and it starts before you understand the situation.
Invalidate the credential first. Investigate second.
That order is counterintuitive, and it is correct. Investigation takes time, and while you investigate, a working credential is in somebody else’s hands.
The short answer
Section titled “The short answer”A vertical sequence: exposure detected, decide revoke or rotate, identify what the credential reaches, create the replacement, update consumers, validate, revoke the old credential, audit for misuse, document.
If nothing depends on the credential, revoke and stop. Most of the complexity below exists only because something does.
Revoke or rotate
Section titled “Revoke or rotate”Two different operations with different risk profiles, and the choice is the first decision.
Revoke — invalidate the credential and do not replace it. Immediate, complete, and it breaks anything that was using it. Correct when: the credential is unused, its purpose is finished, or the breakage is acceptable relative to the exposure.
Rotate — create a replacement, move consumers to it, then revoke the old one. No outage, and a window during which both credentials are valid.
The decision:
| Situation | Do |
|---|---|
| Nothing uses it | Revoke |
| Something non-critical uses it | Revoke, then fix what breaks |
| Something critical uses it, exposure is contained | Rotate |
| Something critical uses it, exposure is public | Revoke now, restore service afterwards |
| You do not know what uses it | Revoke — and treat not knowing as the finding |
Identify what it reaches
Section titled “Identify what it reaches”Before creating a replacement, establish what the credential could actually do. This shapes the severity assessment and the scope of the replacement.
What kind of credential is it? Different types have different revocation mechanisms, different propagation delays and different blast radii.
| Type | Revocation | Notes |
|---|---|---|
| API token | Provider console or API | Usually immediate |
| Cloud access key | Provider IAM | Immediate; check for derived session tokens |
| Database credential | Database, or the secret manager | May require connection draining |
| SSH private key | Remove the public key from every account and host | No usage log in most cases |
| Signing key | Revoke, and consider re-signing | Past signatures still verify |
| TLS certificate | CA revocation, plus reissue | Revocation checking is unreliable in practice |
| Webhook signing secret | Rotate on both sides | Inbound verification breaks until both are updated |
| OAuth client secret | Provider console | Existing tokens may survive; check |
What permissions does it carry? The permissions it was granted, not the ones it uses. Credentials are routinely over-scoped, and the incident is sized by the grant.
What can that reach transitively? A cloud key with permission to read secrets is a key to every secret in that store. A CI token that can trigger deployments is a path to production. Follow the chain at least one level.
Where is it used? This is the question that determines whether rotation is an hour or a week, and if the answer is “unclear”, you have learned why one credential per purpose matters.
Creating the replacement
Section titled “Creating the replacement”Do not recreate the same credential. An exposure is the best opportunity you will get to fix its scope, and nobody will object to the change during an incident.
Narrower scope. Whatever it actually needs, which is usually less than it had.
An expiry. If the type supports one, use it. The next exposure then has a deadline attached.
A different identity where possible. If a person’s token was doing automation’s job, this is the moment to replace it with a GitHub App or a workload identity.
No credential at all, if that is available. For cloud access from CI, OIDC removes the stored credential entirely — see Remove long-lived cloud credentials. An incident is the cheapest time to make that change, because the migration work is happening anyway.
Dual-key rotation
Section titled “Dual-key rotation”Where a provider supports two active credentials at once, rotation becomes a safe, staged operation rather than a cutover.
- Create the second credential. Both are now valid.
- Update consumers to the new one, one at a time, verifying each.
- Watch usage of the old one fall to zero. This is the step that tells you whether your list of consumers was complete.
- Revoke the old one.
- Confirm nothing broke.
Step 3 is the valuable part, and it is why dual-key rotation is worth using even outside an incident. Usage that persists after you have updated everything you know about is a consumer you did not know about, discovered safely rather than through an outage.
Where the provider supports only one credential at a time, the sequence collapses to a cutover, and the risk moves to your knowledge of the consumer list. Practising rotation on a schedule is what makes that list accurate — a credential rotated annually is one whose consumers you know.
Credential types that behave differently
Section titled “Credential types that behave differently”Most of this procedure is provider-neutral. Four categories break the pattern in ways worth knowing before you meet them during an incident.
Signing keys
Section titled “Signing keys”Revoking a signing key stops it being trusted for new signatures. It does not invalidate signatures already made, because nothing in a Git commit or most artefact formats records a trusted timestamp — so “was this signed before or after the compromise?” has no cryptographic answer.
The consequence: an exposed signing key means everything it ever signed is now questionable. The response includes re-signing a known-good state with a new key and telling downstream consumers which key to stop trusting and from when. See Signed Git commits and GPG signing.
TLS certificates
Section titled “TLS certificates”Certificate revocation exists and is unreliable in practice — many clients do not check, and those that do often fail open. Reissuing is necessary and is not sufficient on its own; where the certificate protects something that matters, plan on the assumption that revocation checking will not save you.
Short certificate lifetimes are the durable answer here, for the same reason short token lifetimes are elsewhere.
Webhook signing secrets
Section titled “Webhook signing secrets”These authenticate inbound requests, so they have to be rotated on both sides simultaneously or verification breaks. Where the receiving system supports accepting two secrets during a transition, use it. Where it does not, this is a brief planned outage rather than a seamless rotation — and an exposed webhook secret lets an attacker forge events your system trusts, so it is not one to defer.
SSH keys
Section titled “SSH keys”No usage log in most cases. You can remove the public key from every account and host, but you generally cannot answer “was it used?” the way you can for a token.
Assume the worst, and check what changed in the systems the key could reach rather than looking for evidence of the key itself.
Updating consumers
Section titled “Updating consumers”The list is longer than people expect, and the ones at the end are the ones that cause the 03:00 page.
- Secret managers and vaults
- GitHub Actions repository, organisation and environment secrets — three separate places
- CI/CD systems other than GitHub
- Kubernetes secrets, and anything that mounted them
- Container images with a credential baked in at build time
- Configuration management: Ansible vaults, Terraform variables, Helm values
- Deployment platform configuration
- Local
.envfiles on developer machines - Scheduled jobs and cron entries
- Monitoring and alerting integrations
- Partner and third-party integrations
- Documentation and runbooks that quote it
Two are worth calling out. Container images with a baked-in credential need a rebuild, not a configuration change — rotating the secret does nothing to images already published. And environment secrets in GitHub Actions are separate from repository secrets; updating one does not update the other, and the environment one is usually the production credential.
Validating
Section titled “Validating”- The new credential works. Exercise it against the real system, not a health check that does not authenticate.
- The old credential fails. Use it. Expect
401or403. This is the evidence that the revocation took effect — configuration says it should have; this proves it did. - Everything that used it still works. Not just the parts you remembered.
- The scheduled things. A nightly job that authenticates once a day fails eight hours after you declared the incident closed.
Step 2 is the one that gets skipped, and it is the only step that verifies the thing you actually care about.
Severity, without a scoring framework
Section titled “Severity, without a scoring framework”Incident severity frameworks tend to produce a number that nobody acts on differently. Three questions produce a more useful answer in less time.
Was the exposure reachable by someone outside the organisation? A public repository, a public log, an external chat. If yes, treat it as compromised regardless of anything else — this single question usually determines the response.
What is the worst thing the credential could do? Not the likely thing. A key with permission to delete a production database is a maximum-severity exposure even if the exposure window was four minutes.
Would you know if it had been used? If the provider has no usage log, or retention is shorter than the exposure window, you are working blind and should widen the response accordingly — rotating adjacent credentials, checking for changes in what the credential could reach, rather than only looking for direct evidence.
Those three map cleanly onto action. Externally reachable plus high capability is an incident with a write-up and a review. Internal, low capability, good logs showing no use is a rotation and a note. Anything in between deserves the extra half hour rather than a category.
The one thing not to let the assessment do is delay the revocation. Severity determines what happens after the credential is dead, not whether to kill it.
Auditing for misuse
Section titled “Auditing for misuse”The question the write-up has to answer: was it used by anyone else between exposure and revocation?
Get the timeline. When did the credential enter the exposed location, and when was it revoked? For a Git commit, the commit date is the earliest bound — but if the repository was made public later, that is the moment that matters.
Read the provider’s logs for that window. What to look for is anomalies rather than volume: unfamiliar source addresses, unusual geographies, activity at hours the legitimate consumer does not run, calls to APIs it never uses.
Extend the search to what it could reach. If the credential could read a secret store, check whether it did. A single read of a secrets endpoint is a much larger incident than a hundred ordinary API calls.
Absence of evidence is not evidence of absence. If logging was not enabled, or retention is shorter than the exposure window, say so in the write-up rather than reporting “no misuse found”. They are very different findings.
Documenting
Section titled “Documenting”Short, factual, and aimed at the next person rather than at an auditor.
- What was exposed — type, scope, and what it could reach
- How it was exposed — the mechanism, not the person
- The window — from when to when
- How it was found — this tells you whether your detection works
- What was done — the actions, in order, with times
- Whether it was used — including “we could not tell, because…”
- What would have prevented it — the field that produces change
That last field is where the value is. The honest answer is usually one of a small set: push protection was not enabled, the credential had no expiry, it was over-scoped, it was in a file that should never have contained it, or nobody knew it existed. Each of those has a specific fix, and none of them is “be more careful”.
Making the next one smaller
Section titled “Making the next one smaller”Every exposure is a data point about the system that produced it, and the fix is almost never individual carefulness.
Shorten lifetimes. A credential with a 90-day expiry has a bounded worst case even when nobody notices the leak. This is the single highest-leverage change, because it works without detection.
Narrow scope by default. The incident’s size is the credential’s grant. A token scoped to one repository with read access produces a very different afternoon from one scoped to an organisation with write.
Reduce the number of credentials. Every stored credential is a possible exposure. OIDC, workload identity and short-lived tokens minted per run remove them rather than protecting them.
One credential per purpose. This is what makes rotation an action rather than an investigation.
Keep an inventory. Not an exhaustive one — the credentials that reach production. If a rotation begins with “where is this used?”, that question should have had an answer before the incident.
Rehearse. A team that has rotated a production credential deliberately, on a calm Tuesday, does it in an hour under pressure. A team that never has takes a day and discovers three consumers the hard way.
The pattern across all six: none of them depend on noticing the leak. Controls that require detection work only when detection works, and the point of an exposure incident is that something already got past it.
Common mistakes
Section titled “Common mistakes”Investigating before revoking. The credential works throughout the investigation.
Rewriting Git history first. The expensive half, done first, while the credential stays valid.
Recreating the credential with the same scope. The exposure is the cheapest opportunity you will get to narrow it.
Missing a consumer. Something breaks later, usually a scheduled job, usually out of hours.
Not testing that the old credential fails. Configuration says it was revoked; only using it proves it.
Assuming a private repository means no urgency. The readership is larger and more mutable than it feels, and the credential is valid regardless.
Skipping the audit because nothing looks wrong. “No unusual activity in logs we retained for the relevant window” is a finding. “Nothing happened” is a guess.
Treating “it was only a test credential” as a conclusion. It is a hypothesis. Test what it can reach.
Mental model
Section titled “Mental model”A credential is a capability, not a piece of data. Removing it from a file removes a copy. Revoking it removes the capability — everywhere, at once, including from copies you do not know about. Only one of those is remediation.
What you learned
Section titled “What you learned”- Invalidate first, investigate second; a public exposure is compromised at the moment of exposure
- Revoke when nothing needs it; rotate when something does; not knowing is itself the finding
- Size the incident by the permissions the credential was granted, not the ones it used
- Dual-key rotation lets you watch old-credential usage fall to zero, which validates your consumer list
- The replacement should be narrower, expiring, and ideally a different kind of identity
- Environment secrets are separate from repository secrets, and container images need rebuilding
- Validation means proving the old credential fails, not reading the settings page
- “No misuse found” and “we could not tell” are different findings and must not be conflated
- The most useful field in the write-up is what would have prevented it
Exercise
Section titled “Exercise”Rehearse on something disposable. The point is to time it and find the gaps, not to learn the commands.
-
Create a fine-grained GitHub token scoped to a disposable repository and use it once.
-
Predict: how long does full revocation take, from deciding to confirming failure? Time it.
-
Revoke it and confirm with an API call. Note the exact status code.
-
Now rehearse a rotation: create a second token, switch a script to it, verify, revoke the first. Time this too. Predict: how much longer than a straight revocation?
-
Pick a credential your team actually uses and answer, without looking anything up: what does it reach, where is it used, and who rotates it? Every question you cannot answer is a finding.
-
Check whether it has an expiry. If not, that is the second finding.
Related lessons
Section titled “Related lessons”The secrets management checklist and least-privilege token guide are in the Professional Toolkit.