Skip to content
Tools

How to Detect Secrets in Config Files Before They Leak

· 4 min read

To detect secrets in config files, scan them with a tool that combines known key patterns (such as AKIA for AWS access key IDs or ghp_ for GitHub tokens) with suspicious key names and high-entropy strings, and run it in three places: before you share a file, as a pre-commit hook, and in CI across the full Git history. For a JSON or config snippet you are about to paste somewhere, the JSON secret scanner checks it in your browser.

Where secrets hide in configs

  • .env files committed by accident, or copied into Docker images.
  • appsettings.json, config.json, firebase.json and service-account key files (Google service-account JSON contains a full private key).
  • Connection strings with inline passwords: postgres://app:hunter2@db:5432/prod.
  • Terraform state, Kubernetes manifests and Helm values (Kubernetes Secret data is base64, which is encoding, not encryption).
  • CI configuration, Postman collections and HAR files, which often include Authorization headers.
  • Logs and API responses pasted into tickets, chats and online tools.

How scanners find secrets

TechniqueExampleStrengthWeakness
Provider patternsAKIA[0-9A-Z]{16}, ghp_, sk_live_, xox[bp]-, -----BEGIN PRIVATE KEY-----Precise, names the providerMisses custom or unknown formats
Key namespassword, secret, api_key, token, client_secretCatches generic credentialsFalse positives such as "password": ""
EntropyLong random-looking stringsFinds unknown key typesNoisy on hashes, IDs, base64 data
VerificationTest the key against the provider APIConfirms it is liveSends the secret over the network

Good scanners combine the first three and let you tune the rest with allowlists. Verification is useful in CI but should be a deliberate choice, because it means your secret is sent to a third party for checking.

A worked example

config.json (all values are fake)
{
  "env": "production",
  "aws": { "accessKeyId": "AKIAIOSFODNN7EXAMPLE", "region": "eu-west-1" },
  "db": { "url": "postgres://app:s3cr3t-pass@db.internal:5432/app" },
  "stripe": { "key": "sk_live_REPLACE_ME_0000000000" },
  "featureFlags": { "newCheckout": true },
  "buildHash": "9f86d081884c7d659a2feaa0c55ad015"
}

A scanner should flag the AWS key ID (provider pattern), the database URL (credentials inside a connection string) and the Stripe key (the sk_live_ prefix). The buildHash is random-looking but harmless; it is the classic entropy false positive, which is why key names and context matter. The AWS value shown is the documentation example key, safe to publish.

Tools for each stage

StageToolCommand or setup
Before sharing a snippetJSON secret scannerPaste, review findings, redact, copy
Pre-commitgitleaksgitleaks protect --staged (or the gitleaks git --pre-commit --staged form in newer releases)
Pre-commitdetect-secrets (Yelp)detect-secrets scan > .secrets.baseline, then the pre-commit hook
Full history / CItrufflehogtrufflehog git file://. --only-verified
Full history / CIgitleaksgitleaks detect (or gitleaks git in newer releases)
Hosted reposGitHub secret scanning and push protectionEnable in repository security settings

Command syntax changes between versions, so check --help for the release you install. The important design point is layering: the pre-commit hook stops most leaks on the developer's machine, CI catches anything that bypassed the hook, and history scans find what was committed before you started.

Setting up a pre-commit hook

.pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0   # pin to the release you have reviewed
    hooks:
      - id: gitleaks
pip install pre-commit
pre-commit install          # run on every commit
pre-commit run --all-files  # scan everything once

Keep a reviewed allowlist (gitleaks uses .gitleaksignore and config allowlists, detect-secrets uses a baseline file) so test fixtures do not train the team to ignore warnings.

What to do when a scanner finds a real secret

  1. Revoke or rotate it first. Assume it is compromised the moment it was pushed or pasted anywhere public. Deleting the file does not help: Git history, forks, caches and tool logs keep copies.
  2. Check for use. Review the provider's access logs (for example AWS CloudTrail) for activity you do not recognize.
  3. Remove it from history if the repository is shared, with git filter-repo or BFG, then force-push and ask collaborators to re-clone. This is cleanup, not a fix; rotation is the fix.
  4. Move it to a secret store: environment variables injected at deploy time, a cloud secret manager, or your CI's encrypted secrets. Commit a .env.example with placeholders and add .env to .gitignore.
  5. Add the pattern to your scanner config if it was a format the tools missed.

Sharing configs safely

Much of the real-world exposure comes from sharing, not committing: a config pasted into a public formatter, a log attached to a ticket. Scan and redact before you share. The JSON secret scanner and the Redact option in the JSON formatter replace detected values while keeping the JSON valid, and neither sends your data to a server. For the background, read how to safely format JSON without leaking API keys and the JSONFormatter and CodeBeautify leak explained.

FAQ

How do I find API keys in my code or config files?

Run a secret scanner such as gitleaks, trufflehog or detect-secrets over the files and Git history. For a single JSON snippet, paste it into a browser-based secret scanner.

Is base64 a safe way to store secrets in config?

No. Base64 is reversible encoding. Anyone with the file can decode it, and good scanners decode base64 to look inside.

I committed a secret and deleted it. Am I safe?

No. The value remains in Git history and anywhere it was cloned or cached. Rotate the secret, then clean history if needed.

Why does the scanner flag things that are not secrets?

Entropy checks flag any long random-looking string, such as hashes and IDs. Add reviewed allowlist entries rather than disabling the scanner.

Should secrets live in .env files?

.env files are fine for local development if they are gitignored. In production, inject secrets from a secret manager or your platform's environment settings.

More JSON guides