The Problem: Secrets are the New Gold for Hackers
I’ve seen it happen too many times: a developer is in a rush, they hardcode a temporary AWS key or a database password for a quick test, and before they know it, that key is sitting in a public repository.
It takes less than a minute for automated bots to find that secret and start spinning up expensive instances or dumping your database. In my real-world experience, this is one of the essential skills to master because the cost of a single leaked credential can be catastrophic for a company’s reputation and finances.
The root cause isn’t usually incompetence; it’s human nature. We forget things. We think we’ll remove the key before committing, but we get distracted. Relying on manual code reviews to catch secrets is a losing game. You need an automated safety net that never sleeps.
Quick start: Detecting Secrets in 5 Minutes
Gitleaks is a fast, light-weight tool specifically designed to scan your Git history and current files for secrets. You don’t need a complex setup to get started. If you have Docker installed, you can scan your current project immediately.
docker run -v $(pwd):/path zricethezav/gitleaks:latest detect --source="/path" -v
If you prefer a local binary, you can install it via Homebrew on macOS or download the executable for Linux/Windows. Once installed, running a scan is straightforward:
gitleaks detect --source . --verbose
The detect command looks at your current state. If you want to scan your entire commit history (which I highly recommend for any existing project), use the git mode:
gitleaks detect --source . --log-opts="--all"
When Gitleaks finds something, it will exit with a non-zero code, which is exactly what we need for a CI/CD pipeline to fail and block a deployment.
Deep dive: Integrating Gitleaks into Your Pipeline
Running Gitleaks on your laptop is great, but the real magic happens when you force every pull request to pass a Gitleaks check. Let’s look at how to do this in the two most popular platforms.
GitHub Actions Integration
GitHub makes this incredibly easy with the official Gitleaks action. Create a file at .github/workflows/gitleaks.yml:
name: Gitleaks
on:
pull_request:
push:
branches: [main]
jobs:
scan:
name: Gitleaks Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Notice fetch-depth: 0. This is crucial. By default, many CI tools only fetch the latest commit. Gitleaks needs the full history to ensure no secrets were hidden in older commits that are being merged.
GitLab CI Integration
For GitLab users, you can add a job to your .gitlab-ci.yml file using the Gitleaks Docker image:
gitleaks_scan:
stage: test
image:
name: zricethezav/gitleaks:latest
entrypoint: [""]
script:
- gitleaks detect --source=$CI_PROJECT_DIR --verbose --redact
The --redact flag is a nice touch for CI—it hides the actual secret in the logs so you don’t end up leaking the secret again in your build output while trying to report it.
Advanced Usage: Customizing and Baselining
Not every string that looks like a secret is actually a secret. False positives are the enemy of developer productivity. If your pipeline fails because of a dummy test key, developers will start ignoring the tool.
Handling False Positives with .gitleaksignore
If Gitleaks flags a file or a specific line that you know is safe, don’t just leave it. Create a .gitleaksignore file in your root directory. You can ignore specific Fingerprints (hashes of the finding) provided in the Gitleaks output:
# .gitleaksignore
# Ignore a specific test credential
6f7d8e9a... (your fingerprint here)
Setting a Baseline
If you’re introducing Gitleaks to an old project with thousands of commits, you might find hundreds of “secrets”—some of which are old, revoked, or false positives. You don’t want to fix 500 issues just to start using the tool. Use a baseline:
gitleaks detect --source . --baseline-path gitleaks-baseline.json
This generates a report of all current issues. Gitleaks will now only fail if *new* secrets are introduced. This allows you to adopt a “stop the bleeding” approach before cleaning up the past.
Custom Rules
Every company has unique internal patterns. Maybe your internal API keys always start with MYCORP_. You can define custom rules in a gitleaks.toml file:
[[rules]]
id = "mycorp-api-key"
description = "Detected a MyCorp Internal API Key"
regex = '''(?i)MYCORP_[a-z0-9]{32}'''
keywords = ["mycorp"]
Practical Tips for a Leak-Free Workflow
Implementing Gitleaks in CI is the last line of defense, but it shouldn’t be the only one. Here are some strategies I use to keep my teams safe.
1. Pre-commit Hooks: Catch it Before the Push
Why wait for the CI to fail? Install pre-commit and add Gitleaks to your local workflow. This prevents the secret from ever leaving the developer’s machine.
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.2
hooks:
- id: gitleaks
2. What to do when a secret is found?
If Gitleaks catches a secret in a PR, do not just delete the line and commit again. The secret is still in your Git history. If the secret was ever pushed to a remote server, you must treat it as compromised.
- Rotate: Revoke the key and generate a new one immediately.
- Remove: Use tools like
git-filter-repoor BFG Repo-Cleaner if you absolutely must scrub the history, but rotation is always the priority.
3. Use Environment Variables
This seems obvious, but the best way to avoid Gitleaks failures is to never put secrets in code. Use .env files (and add them to .gitignore) or secret management services like AWS Secrets Manager or HashiCorp Vault. In my experience, if you find yourself needing to ignore a lot of things in Gitleaks, your architectural approach to secrets is likely the real problem.
By automating secret detection, you move from a culture of “hoping for the best” to a proactive security posture. It takes less than an hour to set up, but it might save you from the worst day of your professional life.

