# Keys in git history

> How Cloak Watch finds keys and tokens left in your projects' git history, what each card shows, the opt-in public-leak check, and how to rotate and remove them.

Source: https://docs.subcloak.com/cloak-watch/git-history-secrets/

Added in Cloak Watch 1.2.0.

A key deleted from a project's files is still in its git history, and anyone with a copy of the repository can read it. Cloak Watch reads the history of every project in your code folders for keys and tokens, and raises one [SEC-4](https://docs.subcloak.com/cloak-watch/detections/#sec-4) card per repository. It's free and on by default.

## How it works

- Watch reads each project's `.git` folder directly, on every branch, tag and the stash, newest commits first. It never runs `git`, hooks or anything in the repository.
- It matches about 220 key and token patterns derived from [Gitleaks](https://github.com/gitleaks/gitleaks) (MIT License). Gitleaks' generic "API key" rule is left out because it's too noisy across every project on a Mac.
- Lockfiles, vendored code, `node_modules`, images, fonts, binaries and files over 1 MB are skipped, as in Gitleaks. So are submodules and symbolic links.
- Scans are spread out: a full scan spends about 20 seconds on history, and a repository is read again only when its branches move. Big repositories are read newest first, and the card says if older history wasn't reached.
- Repositories that use SHA-256 object names aren't read yet.

To turn it off, switch off **Look for keys in git history** in [Settings › Privacy](https://docs.subcloak.com/cloak-watch/settings/#privacy), under **Secrets**.

## What a card shows

The card is titled, for example, **2 keys in the git history of shopapp**, at Review. For each key:

- what kind of key it is, the file and line, and the commit (with its date);
- whether the key is **in the current files** or **only in history**;
- the key, masked.

When Watch read only part of a long history, the card says "Watch read the newest N commits; older history wasn't read."

### Masks and fingerprints

Watch never shows, logs, stores or exports a key in full.

- A mask shows a key's first 4 and last 2 characters and its length, for example `ghp_…Q2 (40 chars)`.
- Values under 24 characters, and passwords, show only their length: `•••• (16 chars)`.
- To tell whether two findings are the same key, Watch keeps a fingerprint made with HMAC-SHA256 and a random key that only this Mac has (kept in the Keychain, or in a file only you can read where the Keychain isn't available). A short key can't be worked out from its mask and fingerprint.
- Results are kept in files only you can read.

### The checksum check

GitHub tokens (`ghp_`, `gho_`, `ghu_`, `ghs_` and `ghr_`) and npm tokens (`npm_`) end in a checksum. Watch checks it on your Mac and adds "GitHub's checksum matches" to the key's line, or "fails GitHub's checksum, may be a test value". A key that fails isn't hidden or lowered: check it yourself.

## Is this key public?

On a SEC-4 card, and on a [SEC-1](https://docs.subcloak.com/cloak-watch/detections/#sec-1) card for keys in plain-text files, **Is this key public?** lists the keys with **Check…**. It asks GitGuardian's HasMySecretLeaked whether the key appears in public code. It's opt-in, one key at a time.

1. Click **Check…** next to a key.

2. Watch asks **Check this key with HasMySecretLeaked?** and shows exactly what it will send: the first 5 characters of the key's scrypt hash. GitGuardian also sees your IP address. Click **Check**.

3. The answer is decrypted on your Mac: **Not in public leaks**, or **Found in public code** (with how many times, and **Where** when GitGuardian says where).

The 5 characters are shared by about a million other keys, so GitGuardian can't tell which key you checked. Your Mac sends them directly, never through a proxy or a redirect, and never sends the key, its full hash or where it is. GitGuardian allows 5 free checks a day per network; after that, Watch says to try again tomorrow.

## Fix it

1. **Rotate first.** Make a new key, put it where you use it, and delete the old one at the service that issued it. The card's steps link to the right page, for example **Open github.com**. Rotating is what protects you: history keeps the old value.

2. **Then remove the key from the files** where it's still in the current files.

3. Removing a key from history means rewriting the repository's history (for example with `git filter-repo`) and asking everyone with a clone to re-clone. Do it only after rotating: every existing clone, fork and CI cache keeps the old history.

> **Tip:**
> To check an old key is really dead after rotating, use **Check** in the Verify step of [I think I've been hit](https://docs.subcloak.com/cloak-watch/respond/#check-an-old-key-is-dead).

## Related

  - [Detection catalogue](https://docs.subcloak.com/cloak-watch/detections/#secrets-sec): SEC-1, SEC-3 and SEC-4.
  - [Respond to an incident](https://docs.subcloak.com/cloak-watch/respond/): Revoke and rotate in the right order.
  - [What leaves your Mac](https://docs.subcloak.com/cloak-watch/settings/#what-leaves-your-mac): The Is this key public? row.
