Why We Run TruffleHog: Keeping Secret Keys Out of Your Code

Oct 29, 2026
  • Guides
  • Security
  • Automation

Every app I build talks to other systems: a database with customer details, cloud storage with signed documents and photos, an email service. Each of those doors is opened with a secret key. If one of those keys ends up in the code, and the code ends up on GitHub, anyone who finds it can walk in.

For one of the portals I’m building, I set up TruffleHog, a free, open-source secret scanner, at two checkpoints. I also made a short film about it for everyone involved in the project, technical or not. Watch it below, then read on for the details and the exact setup, ready to copy.

Film / 4:29 Voice-over · captions EN / NL
An animated explainer with voice-over. Use the CC button in the player to switch the captions between English and Dutch.
Read the transcript
  1. How TruffleHog keeps our keys secretOur app uses secret keys to reach customer details, documents and email. This short film shows how a free tool called TruffleHog stops those keys from leaking, and why every team that builds software should use it.
  2. What is a secret?To do its work, our app talks to other systems: the database with customer details, Google Drive with the signed documents and photos, and the email service. Each one is opened with a secret key, just like the key to an office. Whoever holds the key can walk in.
  3. Where the code livesThe app is written on a programmer's own computer. Every change is saved as a snapshot, and the snapshots are uploaded to GitHub, an online place where the team keeps its work. GitHub keeps every snapshot, forever. That is useful, until something ends up in it that shouldn't be there.
  4. An easy mistakeSometimes a key ends up in the code by accident: pasted in while testing, or saved in the wrong file. Nobody notices. It's like writing the alarm code on a sticky note and pinning it to the notice board in the hall.
  5. Robots read everythingCriminals run robots that read new code all day long, looking for keys. In one test, a key placed on GitHub was misused by strangers about two minutes later. In 2024 alone, nearly 24 million secrets were found in public code.
  6. Deleting it doesn't helpDeleting the file afterwards doesn't undo it. The older snapshot still holds the key, and copies may already exist. The only real cure is to change the lock: make a new key and retire the old one. Many companies never do. Of the keys leaked in 2022, 70 percent still worked years later.
  7. Meet TruffleHogThat's why we installed TruffleHog. A truffle pig sniffs out truffles hidden in the ground. TruffleHog sniffs out keys hidden in code. It recognises more than 800 kinds of keys, and we added a rule of our own for the five secret keys of this app.
  8. Checkpoint 1: your own computerThe first checkpoint is on the programmer's own computer. Every time a snapshot is about to be saved, TruffleHog checks it, in less than a second. If it finds a key, the snapshot is refused and the programmer is shown where the key is. The key never leaves the computer. TruffleHog doesn't even send it out to test it.
  9. Checkpoint 2: GitHubThe second checkpoint is on GitHub. Every upload is checked again, in case something slipped past the first one. Every Monday the whole history is checked, back to the very first snapshot. If anything is found, the check turns red and the team gets an email.
  10. We tested itWe tested every part with made-up keys. GitHub flagged the test upload in red. On the computer, the snapshot was refused every time, even when the program used to write the code didn't know where TruffleHog was installed. And the whole history of the project came out clean.
  11. What it doesn't doTruffleHog recognises real keys. It won't flag a line just because it contains the word secret. So good habits still count: keys belong in a private settings file that is never uploaded, or in the settings of the hosting service. Never in the code itself.
  12. Why it's best practiceWhy is this best practice? It costs nothing: it's free and takes less than a second. It prevents expensive accidents, like strangers running up bills on your accounts or reading customer data, and a leak of personal data usually has to be reported to the privacy regulator within 72 hours. And security guidelines, such as OWASP's, say it plainly: secrets don't belong in code.
  13. Use itSo, use it. Install TruffleHog, switch on the checkpoint on your computer, and keep the GitHub check running. If it ever stops you, don't go around it: take the key out. And if a real key does leak, change it straight away and tell the person responsible. A small effort, for a big protection.

The Problem in One Minute

A secret is a key. API keys, database passwords, OAuth client secrets and tokens let your app open another system. Whoever holds the key can walk in, with the same rights as your app.

Git never forgets. Every commit is a snapshot, and GitHub keeps every snapshot, forever. A key that was committed once stays in the history, even after you delete the file.

Leaked keys are found fast. Criminals run bots that scan new public code around the clock. In Orca Security’s 2023 honeypot research, a key exposed on GitHub was misused about two minutes later. GitGuardian counted 23.8 million new secrets in public GitHub commits in 2024 alone.

Deleting is not a fix. The only real cure is to rotate: make a new key and revoke the old one. Many teams never do. According to GitGuardian, 70% of the secrets leaked in 2022 were still valid years later.

How TruffleHog Helps

TruffleHog sniffs out keys hidden in code, the way a truffle pig sniffs out truffles hidden in the ground. It recognises more than 800 kinds of credentials out of the box, and you can add your own detectors for secrets it doesn’t know. I added one for the five keys of this app.

I run it at two checkpoints.

Checkpoint 1: On Your Own Computer (Pre-Commit Hook)

Before Git saves a commit, a hook scans what is staged. It takes less than a second. If it finds a key, the commit is refused and you’re shown the file and line. The key never leaves your machine: the hook runs with --no-verification, so TruffleHog doesn’t even send a candidate to its provider to check whether it’s live.

Save this as .githooks/pre-commit in your repository:

#!/bin/sh
# Scans what is staged for secrets before it becomes a commit.
# Switch it on once per clone with:  git config core.hooksPath .githooks

# Windows: an editor keeps the PATH it started with, so also look where
# TruffleHog is installed by hand.
if ! command -v trufflehog >/dev/null 2>&1 && [ -n "$LOCALAPPDATA" ]; then
  dir="$LOCALAPPDATA/Programs/TruffleHog"
  if command -v cygpath >/dev/null 2>&1; then dir=$(cygpath -u "$dir"); fi
  if [ -x "$dir/trufflehog.exe" ]; then PATH="$dir:$PATH"; fi
fi

if ! command -v trufflehog >/dev/null 2>&1; then
  echo "pre-commit: trufflehog is not installed, staged changes were NOT scanned for secrets." >&2
  echo "pre-commit: install it from https://github.com/trufflesecurity/trufflehog/releases" >&2
  exit 0
fi

# --no-verification: a candidate is never sent out to test whether it is live.
trufflehog git file://. \
  --since-commit HEAD \
  --trust-local-git-config \
  --no-verification \
  --results=verified,unknown,unverified \
  --fail \
  --no-update
status=$?

if [ "$status" -eq 183 ]; then
  echo "pre-commit: TruffleHog found a secret in the staged changes. Commit refused." >&2
  exit 1
fi
if [ "$status" -ne 0 ]; then
  echo "pre-commit: TruffleHog could not finish the scan (exit $status). Commit refused." >&2
  exit 1
fi

Then switch it on, once per clone:

git config core.hooksPath .githooks

A few choices worth explaining:

  • Unverified results count. Many of an app’s own secrets, such as a database service-role key or a refresh token, can’t be confirmed against a public API. A check that only blocked confirmed-live keys would let those through.
  • No TruffleHog, no block. If TruffleHog isn’t installed, the commit goes ahead with a warning, because the GitHub check below still scans every push. A real finding always blocks.
  • False positives. Put a trufflehog:ignore comment on that line. Never skip the check with git commit --no-verify.
  • Teammates on macOS and Linux. If you create the hook on Windows, mark it executable in Git with git update-index --chmod=+x .githooks/pre-commit.
  • Your own secrets. To add detectors for keys TruffleHog doesn’t know, put them in a .trufflehog.yml and add --config .trufflehog.yml to both checkpoints.

Checkpoint 2: On GitHub (GitHub Actions)

The second checkpoint catches anything that slipped past the first: a teammate without the hook, or a commit made with --no-verify. Every push, on any branch, is scanned for the commits it adds. Every Monday, and whenever you start it by hand, the whole history is scanned, because a detector added to TruffleHog since the last run may find what an earlier scan couldn’t.

Save this as .github/workflows/secret-scan.yml:

name: Secret scan

on:
  push:
  schedule:
    - cron: "40 4 * * 1" # Mondays: the whole history
  workflow_dispatch:

permissions:
  contents: read

jobs:
  trufflehog:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - name: Checkout
        uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: TruffleHog
        uses: trufflesecurity/trufflehog@e76dff8a7e57d6df0c60ad8cbf5d5cba505a8fc9 # v3.99.2
        with:
          version: 3.99.2
          extra_args: --results=verified,unknown,unverified

If anything is found, the run turns red with exit code 183 and an annotation that points at the file and line, and GitHub sends an email about the failed run. Both actions are pinned to a commit, so the check never runs whatever happened to be published last.

We Tested It

With made-up keys that never worked anywhere:

  • A test upload to GitHub: the check turned red.
  • A key in a commit: refused on the computer, nothing sent out.
  • An editor that couldn’t find TruffleHog on its PATH: still refused.
  • The project’s whole history: clean, no keys found.

What It Doesn’t Do

TruffleHog recognises real keys in real formats, plus any detectors you add. It won’t flag my secret: 1234 just because the word “secret” is in it. So good habits still count. Keys belong in:

  • a local settings file such as .env that is listed in .gitignore and never uploaded, or
  • the environment variables of your hosting service, such as Netlify, Vercel or Azure.

Never in the code itself.

Why It’s Best Practice

  • It costs nothing. TruffleHog is free and open source, and a check takes less than a second.
  • It prevents expensive accidents. Misused cloud accounts, surprise bills, exposed customer data. Under the GDPR, a personal-data breach usually has to be reported to the regulator within 72 hours.
  • It’s what security experts advise. The OWASP Secrets Management Cheat Sheet says it plainly: keep secrets out of code, and detect them automatically.

If a Key Does Leak

Act as if it’s already being used:

  1. Rotate it straight away. Create a new key, update your app, and revoke the old one.
  2. Tell the person responsible, and check the provider’s logs for activity you don’t recognise.
  3. Only then consider cleaning the Git history. Rewriting history doesn’t make an exposed key safe again; rotating it does.

Your Turn

  1. Install TruffleHog: brew install trufflehog on macOS, or download it from the official releases page for Windows and Linux.
  2. Switch on the checkpoint: add the hook and run git config core.hooksPath .githooks on every computer.
  3. Keep the GitHub check on: every push, and every Monday.

If TruffleHog ever stops you, don’t go around it: take the key out. A small effort, for a big protection.

Want this set up across your team’s repositories, or a quick check of what is already in your Git history? Get in touch and I’ll help you put both checkpoints in place.

Sources

Free 30-min call · No obligation

Ready to automate your business?

Let's talk about what's slowing you down — and build something that runs itself.

Book a free 30-min call