An API key, a token or a password is in a commit, a build log or a public paste, and it should not be. Here is the order to do things in — and why deleting the commit is neither step one nor step two.
Rotate first, clean up second. The only action that ends the exposure is invalidating the credential where it was issued. Rewriting history, deleting the commit or flipping the repository to private all change how conveniently someone can find the secret. None of them change whether it still works.
Assume it was already collected. Public pushes are watched continuously by people who are not you. Plan as though a stranger has had the key since the moment it was pushed, because the cost of being wrong in that direction is a few minutes of rotation, and the cost of being wrong in the other direction is your bill.
Revoking the credential cuts every one of those at once, because it makes the copies worthless instead of hunting them. Rewriting history cuts none of them. That is the entire argument for the order below.
.env that was never ignored, a config committed “just
to get CI green”, a debug print of the whole environment — usually put others
somewhere too. Scan the whole history, not just the tip.All four of these are ordinary, documented behaviour, not exotic attacks.
Force-pushing moves the branch. It does not immediately destroy the object. On a hosted service the old commit typically stays fetchable by its full SHA until the server garbage collects, and anyone who saw the push already has the SHA. “It is gone from the branch” and “it is gone” are different sentences.
On a hosted fork network, objects live in a shared pool. A commit removed from the repository you control can remain reachable through a fork you do not control — including one created after you noticed. You cannot force-push someone else's fork.
Public activity feeds publish push events, and there are public archives of that firehose. Automated collectors read it in real time looking for exactly this. The window between pushing a secret and someone else holding it is not measured in hours.
If the secret shipped in anything — a container image layer, a published package tarball, a client-side bundle, a CI log, a cached page — deleting the source commit does not touch those copies. Each one is a separate cleanup with its own deadline.
If you are not sure what you are holding, the field guide to credential prefixes identifies it from the first few characters. Then:
| Credential | Where it dies, and what to watch |
|---|---|
AWS access keyAKIA… |
IAM → the user → security credentials: deactivate the key, then delete it. Read CloudTrail filtered on that access key id. Unfamiliar regions are the classic tell — compute spun up somewhere your team has never deployed. Check for new IAM users or keys created after the exposure; that is how a short visit becomes a long one. |
GitHub tokenghp_…, github_pat_… |
Developer settings → personal access tokens → delete. The token page shows a last-used date, which is a fast first answer. Organisation owners also have an audit log. Note that some providers partner with code hosts to auto-revoke keys detected in public repositories, so yours may already be dead — verify that, do not assume it. |
Payment or billing API keysk_live_… |
Roll it in the provider dashboard rather than deleting it outright if there is a grace-period option, so live traffic does not fail while you deploy. Then read the API request log for calls you cannot account for. |
Model-provider keysk-…, sk-ant-… |
Revoke on the API keys page. These are stolen for resale and the abuse shows up as spend, so the usage page is your audit log. Set a spending limit while you are there. |
Google service account.json key file |
IAM & admin → service accounts → keys → delete the key id. Then look at Cloud Audit Logs for that principal. Also ask what the account could reach: a service account is often over-granted, and the blast radius is the point. |
Slack / chat bot tokenxox… |
Revoke in the app's OAuth settings and reinstall the app. Bot tokens read history, so treat the channels it could see as exposed too. |
Private key-----BEGIN … PRIVATE KEY----- |
There is no issuer to call. Generate a new pair and replace it everywhere the public
half is trusted — authorized_keys, certificates, deploy keys. If it signed
tokens, every token it ever signed must now be treated as forgeable: publish a new key id
and stop accepting the old one. This is the most expensive case on this page. |
| Database URL with a password | Change the password, then check the server's connection log for sources you do not recognise. If the database was reachable from the public internet, that is the finding, and it outlives this one key. |
The order is the same — rotate, check, clean — but “clean” means different things and some of them are not available to you at all.
| Where it leaked | What cleanup actually means |
|---|---|
| CI build log | Delete the run, and mark the variable as secret so it is masked next time. Masking only works if the runner knows the value, which is why an echoed derived value still prints in the clear. |
| Chat, issue tracker or AI assistant | Editing removes the current version; the edit history, notification emails and any retention on the provider's side are not yours to delete. Rotate and move on. |
| Pastebin or gist | Deletion works if you get there first, and search engine and archive caches are the reason you often do not. |
| Published package or image | Publish a fixed version, then handle the old one by the registry's rules. Yanking hides it from resolution; it does not always remove the bytes, and anyone who already installed it has them. |
| Client-side bundle or mobile app | It was public the moment it shipped. Anything embedded in a client is not a secret — the fix is architectural: move the call behind something you control. |
| Screenshot or screen recording | People blur keys and forget the terminal title bar, the browser tab and the notification that appeared mid-recording. Re-watch it before you decide only one thing leaked. |
Two of these are free tools on this site; the third is a habit that costs nothing.
Scrub the log before it leaves your machine Paste a log, a stack trace or a config dump and get it back with the credentials replaced by numbered placeholders, so it is still readable enough to debug from. It runs entirely in your browser — nothing is uploaded. If you issue the tokens, give them a prefix A distinctive, greppable prefix and a checksum are what let scanners — yours and everyone else's — find your token in a haystack and tell it apart from a hash. It is the single cheapest thing a token format can do for the people who leak it.And the habit: decide that logs, screenshots and pastes are public by default, and check them on the way out rather than after. The two-minute version of that check is here.
This is about a credential leaking as text that you can point at. It is not about a compromised machine, a malicious dependency or a breach at the provider — in those cases you do not know what the attacker holds, and the response is a different and larger one.
It also stops at the technical response. If the credential could reach personal data or customer data, whether you are obliged to tell anyone is a question for your organisation and its lawyers, not one I will answer. I have written the engineering half here because the engineering half is where the clock is running.