Committed a secret to GitHub: now what?
TL;DR:
- A secret pushed to GitHub is compromised the instant it lands, so rotate the credential first.
- History rewriting removes the value from commits but does not un-leak it; it is cleanup, not containment.
- Work in order: rotate, then purge history, then verify across clones, forks, and caches.
- Add a secret scanner to pre-commit and CI so the next leak never merges.
What is the first thing to do after committing a secret?
Rotate the leaked credential at its source before touching git history. Automated scrapers monitor public GitHub events and can grab a new key within seconds of the push, so the window where rotation matters is tiny. Rotation invalidates the leaked value, which is the only action that actually stops an attacker using it. Removing the commit, making the repo private, or force-pushing a clean branch all leave the real credential live and usable. This ordering mistake, reaching for history surgery first, is the single most common way teams turn a contained leak into an incident.
This guide is part of our DevSecOps guides for AI workloads hub and pairs closely with least privilege for AI agents, because a tightly scoped token limits the damage even when one does leak.
Why is rotation more important than removing the commit?
Rotation is containment; history removal is hygiene. Once a secret is public, you cannot prove who copied it, so you must assume it is in hostile hands. The leaked string is no longer secret, only the new rotated value is. Force-pushing a rewritten history feels decisive, but the original commit can still live in forks, in collaborators’ local clones, in CI build logs, and in GitHub’s own cached commit views that survive a rewrite. None of those are reachable by your force-push. The OWASP GenAI security project treats credential exposure as a high-severity issue precisely because the leaked value keeps working until it is revoked.
What are the exact steps to remediate a leaked secret?
Follow these steps in order. Do not skip ahead to the history rewrite.
- Identify the exact secret and its scope. Note what the credential unlocks, which provider issued it, and whether it appears in more than one file or commit. This decides how aggressively you must rotate.
- Rotate or revoke the credential at the provider. Generate a new key, revoke the old one, and update your deployment environment and secret store with the new value. The leaked key must be dead before you continue.
- Audit for abuse during the exposure window. Check provider access logs, billing, and audit trails for unfamiliar activity between the push time and the revocation time. Treat any anomaly as a separate incident.
- Remove the secret from working files. Move it into an environment variable or secret manager so the rewrite does not simply re-introduce it on the next commit.
- Purge the secret from git history. Use a history-rewriting tool such as
git filter-repoor the BFG Repo-Cleaner, following the official GitHub guidance on removing sensitive data from a repository. Force-push the cleaned history. - Invalidate existing clones and caches. Ask collaborators to re-clone, delete stale forks, and contact GitHub Support to expunge cached views of the leaked commits if the exposure is sensitive.
- Verify the leak is gone. Re-scan the repository and its history with a secret scanner, and confirm the rotated credential is the only one that authenticates.
Which tools should I use to scan and verify?
Use a dedicated secret scanner for both detection and post-fix verification. Two widely used open-source options are gitleaks and TruffleHog. The table compares how they fit this workflow.
| Tool | Best for | Runs in pre-commit | Verifies live credentials |
|---|---|---|---|
| gitleaks | Fast pattern-based scanning of commits and history | Yes | No |
| TruffleHog | Detecting and verifying whether a found key is still active | Yes | Yes |
To verify remediation, run gitleaks on a full-history scan: after a git filter-repo purge plus rotation it should report no findings. Always run the scan against your own history, because a clean result on a sample repository tells you nothing about yours.
Does the purge actually work? (a first-hand check)
On 21 June 2026, using gitleaks 8.30.1, I ran this exact workflow against a disposable repository to confirm each step behaves as described. A dummy AWS-style credential pair was committed to a config.env file, then the history was purged.
# 1. Detect: scan the full git history
gitleaks git . --report-format json --report-path report.json
# -> 1 finding, RuleID "generic-api-key", config.env line 2
# 2. Purge: redact the secret across all history
printf 'AKIA...==>REDACTED\nwJ8x...==>REDACTED\n' > replace.txt
git filter-repo --replace-text replace.txt --force
# 3. Re-scan to confirm
gitleaks git .
# -> "no leaks found" (exit 0); grep of git log --all confirmed 0 occurrences
Two findings worth passing on. First, detection is only as good as the pattern: an earlier dummy key that contained the substrings “TEST” and “KEY” was silently skipped, because gitleaks allowlists common placeholder words to cut false positives. A real-looking key was flagged immediately. Second, git filter-repo rewrites every commit hash, so after the purge you must force-push and every collaborator must re-clone. Rotating the credential remains step one regardless, because anything already pushed should be treated as public.
How do I prevent the next leak?
Shift detection left so a secret is blocked before it ever reaches GitHub. Install a scanner as a pre-commit hook so a flagged credential fails the commit locally, and add the same scan as a required CI check so nothing merges without passing. Combine this with the permission boundaries described in least privilege for AI agents and the controls in the AI agent hardening checklist, so that even a leaked token is short-lived and narrowly scoped. The defensive layering principle aligns with NSA and CISA hardening guidance: assume any single control can fail, and make the blast radius small when it does.
For the full set of related controls, return to the DevSecOps guides hub and map your leak against the relevant entry in the OWASP LLM Top 10.