---
title: "Secrets in git history: finding them before someone else does"
url: "https://cs.abinantony.io/blog/secrets-in-git-history"
category: Secure coding
published: 2026-01-30
updated: 2026-01-30
type: article
source: Abin Antony Security
---

# Secrets in git history: finding them before someone else does

> To handle secrets in git history, scan the full history rather than the working tree, rotate every credential you find because deletion does not remove it from clones, then add pre-commit and CI secret scanning so the next one is blocked before it lands. Rewriting history is optional; rotation is not.

Almost every code review I run finds at least one credential in git history. Usually it is old, usually someone noticed and removed it in a later commit, and usually nobody rotated it — because removing it felt like fixing it.

It is not. Every clone of the repository still has it. Every fork, every CI cache, every laptop that pulled that branch, and any mirror a service made while it was there.

## Scan the history, not the checkout

A grep of the current tree finds nothing, which is exactly why teams believe they are clean. Scan every commit on every branch.

```bash
# Full-history scan, not just HEAD
gitleaks detect --source . --log-opts="--all --full-history"

trufflehog git file://. --json --only-verified

# Quick sanity check on what a scanner might miss
git log --all --full-history -p | grep -nE "BEGIN (RSA|OPENSSH|EC) PRIVATE KEY"
```

- Run it against every branch, including ones nobody has merged.
- Include submodules and any vendored directories.
- Check the CI configuration and its logs — build output is a common secondary leak.
- Look in test fixtures. Real credentials end up in them constantly, on the theory that test data does not matter.

## Rotate first, argue about history later

The response order matters, because the window between discovery and rotation is the only part you control.

1. Rotate the credential. Immediately, before writing the incident note.
2. Check what it could reach and review the logs for that period — the useful question is not whether it leaked, but whether it was used.
3. Then decide whether to rewrite history. For a public repository, yes. For a private one with rotation done, it is hygiene rather than remediation.
4. If you do rewrite, coordinate it: everyone re-clones, and any fork that does not is still holding the old objects.

> If a credential cannot be rotated without downtime, that is a second finding. Anything you cannot rotate under pressure will not be rotated when it matters.

## Stop the next one

Detection after the fact is a losing game played forever. Two gates end most of it.

- Pre-commit hooks that block obvious secrets locally. Fast feedback, and developers stop fighting it once it never false-positives on their code.
- CI scanning on every pull request, failing the build on a verified secret. The build failure is what makes it real.
- A .env.example convention plus a secret manager, so nobody needs a real value in the repository to run the app.
- Scoped, short-lived credentials wherever the platform offers them — OIDC federation for CI beats any static key you have to protect.

## Tune it or lose it

A scanner that fires on every random hex string gets disabled within a month, and then you have neither scanning nor the illusion of it. Spend the hour: allowlist the known false positives, prefer verified-only detection where the tool supports it, and make the failure message say exactly which file, which line, and what to do next.


---

Abin Antony Security — Abin Antony, Kochi, Kerala. Contact: abina35@gmail.com · https://cs.abinantony.io
