secrets-leak-remediation
verified5cff8b33-1b24-4aa7-88b2-7b0bf97841b9
Respond to a leaked secret — rotate immediately, purge from git history, and prevent recurrence. Use the moment a secret is found committed or exposed.
Metadata
Skill file
# Secrets Leak Remediation
Use when a secret (API key, token, password, private key) has been committed to a git repository — whether 5 minutes ago or 5 months ago. This is an incident response workflow.
## Step 1: Rotate Immediately (ALWAYS FIRST)
**Do not wait. Do not "investigate first."** The moment a secret leaves your machine, assume it is compromised.
```bash
# Example: revoke a GitHub Personal Access Token
# Go to https://github.com/settings/tokens → delete the leaked token → generate new one
# Example: rotate an AWS access key
aws iam delete-access-key --access-key-id AKIA...LEAKED
aws iam create-access-key --user-name my-service
# Example: rotate a database password
psql -c "ALTER USER myapp WITH PASSWORD 'new-password-here'"
```
After rotation, **update wherever the secret is used** (env vars, CI secrets, config files). Do NOT put the new secret in the repo.
## Step 2: Purge from Git History
### Option A: git-filter-repo (recommended)
```bash
# Install
pip install git-filter-repo
# Create a replacements file (secret → REDACTED)
cat > secrets.txt << 'EOF'
leaked-api-key-abc123==>REDACTED
EOF
# Purge from ALL history
git filter-repo --replace-text secrets.txt --force
# Force push to remote (COORDINATE WITH TEAM FIRST)
git push origin --force --all
git push origin --force --tags
```
### Option B: BFG Repo-Cleaner (if git-filter-repo unavailable)
```bash
# Download BFG jar
wget https://repo1.maven.org/maven2/com/madgag/bfg/1.14.0/bfg-1.14.0.jar
# Replace the secret string
java -jar bfg-1.14.0.jar --replace-text secrets.txt
# Clean up and force push
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push origin --force --all
```
### For a Single File (recent commit, no history spread)
```bash
# If the secret was only in the latest commit and that file
git reset HEAD~1
# Remove the secret from the file, then:
git add .
git commit -m "fix: remove leaked secret"
git push --force # Still requires coordination
```
## Step 3: Notify Collaborators
After force-pushing, every team member must:
```bash
git fetch origin
git reset --hard origin/main # Or their branch
git clean -fd # Remove untracked files
```
**Send a clear message**: "Force-pushed to purge a leaked secret. Please reclone or reset to origin. Your local branches will be out of sync."
## Step 4: Prevent Recurrence
1. **Add the pattern to gitleaks config** so it's caught next time:
```toml
# .gitleaks.toml — add a custom rule
[[rules]]
id = "my-org-secret-pattern"
description = "Custom secret pattern"
regex = '''myorg_[a-z0-9]{32}'''
```
2. **Move the secret to a secure store**: `.env` (gitignored), GitHub Secrets, Vault, AWS Secrets Manager.
3. **Enable pre-commit hook**:
```bash
pre-commit install
# Or: gitleaks protect --staged
```
4. **Add .gitignore for common secret files**:
```gitignore
.env
.env.*
*.pem
*.key
credentials.json
secrets.yml
```
5. **Set up CI scanning** (see `credential-and-key-scanning` skill).
## Guardrails
- **Never** purge history WITHOUT rotating the secret first — the secret is already out there.
- **Never** force-push to shared branches without warning every collaborator.
- **Never** assume a leak on a private repo is safe — private repos get forked, cloned, and screenshotted.
- **Never** put the new secret in the same place the old one leaked from.
- **Always** verify rotation by trying the old secret (it should be rejected).
## Pitfalls
- **Purging history without rotating**: The secret remains valid and exploitable. Purging only hides it from the repo, not from anyone who already has it.
- **Force-pushing without warning teammates**: They'll push their old history back, re-leaking the secret.
- **Forgetting to check all branches**: A secret in a feature branch, tag, or fork is still exposed. Scan and purge everywhere.
- **Using `git filter-branch`**: Deprecated, slow, error-prone. Use `git-filter-repo` instead.
- **"Nobody saw it" denial**: You can't prove nobody cloned in the window. Always rotate.
## Verify / Checklist
- [ ] Old secret has been revoked/rotated (confirmed at provider — not just "I clicked revoke")
- [ ] New secret works and is stored in a secure location (env var, secret store), NOT in the repo
- [ ] `gitleaks detect` shows zero findings for the rotated secret across all branches
- [ ] Force push completed and all collaborators notified
- [ ] Pre-commit hook installed and working
- [ ] CI pipeline includes secret scanning
- [ ] `.gitignore` updated for common secret files
- [ ] Incident documented: what leaked, when, who was notified, what changed to prevent recurrence
Attached files
No attached files.