Recover a secret that was committed and pushed
Task
Commit a secret to a repository, push it, and then carry out the correct remediation -- which is not "delete it in the next commit". This is the incident every team meets, and getting the order right matters: rotate first, clean history second, and understand why the deletion commit achieves nothing.
Steps
- Commit an application config containing an API key, and push it to the "remote". Confirm it is there with
git log -p. - Make the naive fix: remove the key in a new commit and push. Then prove it did nothing for the exposure -- the key is still in the earlier commit, and
git log -pandgit show <first-hash>both still contain it. - State the order of correct remediation before doing it: the key is disclosed the moment it is pushed, so ROTATE it first. Simulate rotation by generating a new key and updating the config.
- Clean the history: use
git filter-repo(or the olderfilter-branch/BFG) to remove the file from every commit. Confirm the key no longer appears anywhere ingit log -p. - Force-push the rewritten history and explain, in one sentence, why every other clone is now inconsistent and must re-clone.
- Prevent a recurrence: add a pre-commit hook or
gitleaksthat refuses to commit a file matching a secret pattern, and prove it blocks a new attempt. - Add the secret's path to
.gitignoreand confirm a fresh secret file is no longer even a candidate for committing.
Verify
cd ~/lab-secret/work
git log -p | grep -c 'API_KEY_VALUE' # non-zero before cleaning
# after filter-repo:
git log -p | grep -c 'API_KEY_VALUE' # 0
git log --all --oneline | wc -l # history rewritten
echo 'API_KEY_VALUE=x' > new.env && git add new.env 2>&1 | grep -qi 'secret\|blocked' && echo "hook blocks secrets"
The count going from non-zero to zero across filter-repo is the history clean. But the lab's real lesson is the order: by the time you are cleaning history the key has already been disclosed, so rotation in step 3 is the actual remediation and everything else is tidying up after it.
Notes
"Removing a secret from the latest commit" is the reflex to unlearn. The secret is in every earlier commit and in every clone anybody has taken; the deletion commit only stops it being in the tip. A pushed key is a disclosed key -- rotate it, then clean up.