DEV Community

Eze
Eze

Posted on

Your 'Deleted' Private Key Is Still in Your Git History

You removed the secret from your code. You pushed the fix. You're safe.

You're not.

The key still lives in your git history — commit a3f92e1, six months back — and public scrapers and sweeper bots read that history every day. Roughly 40% of the $16.7B lost to crypto hacks comes from compromised private keys (DeFiLlama via CoinDesk), and GitGuardian's research shows most leaked secrets stay valid for years after disclosure.

I built a tool for exactly this problem. It's called drainscan — open source, MIT — and this post shows it catching a "deleted" secret in under two seconds.

The trap: working-tree scanners miss history

Here's the classic incident, replayed. Someone commits a real key:

# config.py  (commit 1)
DATABASE_URL=postgres://localhost/dev
PRIVATE_KEY=0x8f2c...d4c0
Enter fullscreen mode Exit fullscreen mode

Then someone notices and removes it:

# config.py  (commit 2)
DATABASE_URL=postgres://localhost/dev
Enter fullscreen mode Exit fullscreen mode

Scan the current tree with anything and you get a green light. The key is gone. Except it isn't:

$ git show HEAD~1:config.py | grep PRIVATE_KEY
PRIVATE_KEY=0x8f2c...d4c0   # still right there
Enter fullscreen mode Exit fullscreen mode

What drainscan does differently

drainscan detects EVM private keys, Solana keys (base58 and Phantom-style JSON arrays) and BIP-39 mnemonics — then does two things generic scanners can't:

  1. Derives the address offline. Your key material is never transmitted anywhere; you learn which wallet leaked without exposing it further.
  2. Checks balances read-only (--live). A leaked key with 0 balance is noise; one holding funds is an emergency. drainscan tells you which.

It also kills false positives with context scoring: transaction hashes are 64 hex chars too, so tx_hash = 0x9fc7... gets downgraded to low confidence, while PRIVATE_KEY= context scores high. BIP-39 checksum validation means random English sentences never trigger alerts.

Demo: catch the deleted key

Working-tree scan on the repo where we just "fixed" the leak:

$ drainscan scan . --min-confidence high
No key material found. You're clean (at detected patterns).
Enter fullscreen mode Exit fullscreen mode

Now the deep scan (Pro feature):

$ drainscan history --max-commits 10
Deep-scanning git history (up to 10 commits)...

1 finding(s):

  [!!] evm_key - config.py @ 2ad95e6c:0 (high)
        secret: a67296...141d (redacted)
        address (ethereum): 0x8a5be301cb283f2e32496c4953b2ceab17b59867

exit code: 1
Enter fullscreen mode Exit fullscreen mode

That address matches the planted key exactly. Exit code 1 means CI can fail the build — wire it into GitHub Actions and no PR that reintroduces key material can merge.

Free vs Pro

Everything above runs free forever: working-tree scanning, offline derivation, balance checks, confidence scoring, CI exit codes, pre-commit hooks.

Pro ($99 one-time during intro) adds what teams need: full-history deep scans at any depth, SARIF reports that surface findings directly in GitHub code scanning UIs, and standalone HTML reports for security review meetings.

Licenses are Ed25519-signed files verified locally — no account, no telemetry, no phone-home.

Try it

pip install git+https://github.com/ezequiellich44-cmd/drainscan.git
drainscan scan .
Enter fullscreen mode Exit fullscreen mode

Run it against your oldest repo. If you've ever pasted a private key into config while debugging, you probably already know what it will find — better you than a sweeper bot.

If drainscan finds a funded key: move the funds to a fresh key immediately and treat the old one as burned. Rotation is the only fix.

Top comments (0)