Is Your API Key Already Public? How to Stop Secret Leaks

How API keys leak through git, CI logs, bundles and AI agents, and how to stop it with gitleaks, push protection, OIDC and a tested incident playbook.

TheTechLane cover on deep aubergine: a neon-lime git commit graph where a coral key on one commit is caught in a scanner net, with a prominent TL logo and the headline asking if your API key is already public

Most leaked API keys are not stolen by hackers. They are published by developers, one ordinary commit at a time.

A forgotten .env file, a debug print, a key pasted into an AI chat, or a token baked into a frontend bundle is all it takes.

The scale is real: GitGuardian's State of Secrets Sprawl 2026 report counted 28.65 million new hardcoded secrets in public GitHub commits in 2025, a 34% jump year over year.

The same report found that more than 64% of valid secrets it detected in 2022 were still valid when retested in January 2026.

In this guide, you'll learn how to:

  • Spot the six most common ways keys escape

  • Catch secrets before they land with gitleaks and GitHub push protection

  • Set up a pre-commit hook in under ten minutes

  • Replace long-lived keys with a secret manager and OIDC short-lived credentials

  • Run a calm, ordered playbook when a key does leak

Let's start with the threat itself.


Quick Answer

Threat in one line: any credential that touches a file, log, bundle, or chat can be copied, and git remembers it forever.

Fix in one line: block secrets at commit and push time, keep them out of code entirely, and when one leaks, revoke it first and clean up second.

The defence layers look like this:

Developer laptop  →  pre-commit hook (gitleaks)
        ↓
git push          →  GitHub push protection
        ↓
Repository / CI   →  secret scanning + CI scan
        ↓
Runtime           →  secret manager + short-lived OIDC credentials
        ↓
Leak happens      →  revoke → rotate → audit → purge history

How Secret Leaks Actually Happen

Leaks rarely look dramatic.

They look like normal work.

1. Git history

Deleting a key in a new commit does not remove it.

The old commit still contains it, and anyone who clones the repository gets the full history.

2. Committed .env files

A missing .gitignore entry plus git add . is the classic leak.

GitHub's own guidance on removing sensitive data recommends avoiding catch-all commands like git add . and reviewing staged changes with git diff --cached.

3. CI logs

Build steps that echo environment variables, run with verbose flags, or print failed HTTP requests can write a token straight into a log that many people can read.

Masking helps, but GitHub's secure use reference for Actions warns that redaction relies on exact matches, so structured values like JSON or YAML blobs can slip through.

4. Client bundles

Anything shipped to the browser is public.

Build tools make this explicit: Vite only exposes variables prefixed with VITE_ to client code, precisely because those values end up in the bundle.

A secret key behind a VITE_ or NEXT_PUBLIC_ prefix is a published key.

5. AI agent context

Coding agents read your files, terminals, and config.

If a key sits in a prompt, an MCP config, or an agent-generated commit, it can travel further than you expect.

The GitGuardian report found commits co-authored by Claude Code leaked secrets at about 3.2%, versus a 1.5% baseline, and counted 24,008 unique secrets in MCP-related configuration files.

We saw a related agent privacy slip recently in Is Claude Code Handing Your Email to Every Site It Visits?.

6. Breaches at hosting platforms

Sometimes you do everything right and the platform storing your environment variables gets breached.

That is exactly what happened in Northflank Breach Exposes AI Keys: Have You Rotated Your Secrets Yet? and Trigger.dev Breach Exposes Env Vars: Are Your Secrets Already Out?.

Long-lived keys turn a vendor breach into your breach.


Who Is at Risk?

Short answer: every team that ships code.

The highest-risk setups are:

  • Solo developers and side projects pushing to public repos

  • Startups with shared .env files passed around in chat

  • Teams using AI coding agents with broad file and terminal access

  • CI pipelines that store cloud keys as long-lived repository secrets

  • Frontend apps calling paid APIs (LLMs, maps, payments) directly from the browser

Private repositories are not a safe place either.

GitGuardian found internal repositories roughly six times more likely than public ones to contain hardcoded secrets.


How to Detect Leaked Secrets

Detection works best in layers: on your laptop, at push time, and across history.

Gitleaks

Gitleaks is an open-source scanner for passwords, API keys, and tokens in git repos, files, and stdin.

Install it on macOS or Linux with Homebrew:

brew install gitleaks

Scan the full history of the current repository:

gitleaks git -v

Scan a folder that is not a git repo (or your working files):

gitleaks dir -v .

Note: since v8.19.0, the older detect and protect commands are deprecated in favour of git, dir, and stdin.

GitHub secret scanning and push protection

GitHub push protection blocks pushes that contain supported secrets before they reach the repository.

Two flavours exist:

  • Push protection for users is on by default on GitHub.com and stops you pushing secrets to public repositories.

  • Push protection for repositories must be enabled by an admin, requires GitHub Secret Protection, and records bypasses as alerts.

Bypasses are allowed with a reason, so treat a bypass alert as a real event, not noise.

TruffleHog

TruffleHog is useful for checking whether a found credential is still live.

Scan a local repo and show only verified secrets:

trufflehog git file://. --results=verified

A verified result means the key still works, which moves it to the top of your incident list.

UML sequence diagram: a developer commits a staged .env file with an API key, the gitleaks pre-commit hook fails the commit, the developer removes the key and commits cleanly, then GitHub push protection scans the push and either blocks a secret or accepts the clean push

How a leaked key gets stopped before GitHub


How to Prevent Secret Leaks

Detection catches mistakes.

Prevention removes the reason to make them.

1. Ignore secret files from day one

Add .env, *.pem, and local credential files to .gitignore before the first commit, and commit an .env.example with fake values instead.

2. Stage deliberately

Use git add <file> or git add --interactive, and check git diff --cached before every commit.

3. Run a scanner on every commit

A pre-commit hook makes scanning automatic (walkthrough below).

4. Turn on push protection

Enable it at the repository or organization level so the server is your second net.

5. Keep secrets out of the browser

Route paid API calls through your backend or an edge function, and expose only publishable keys to the client.

6. Move secrets into a secret manager

The OWASP Secrets Management Cheat Sheet recommends centralising secrets, automating rotation, and keeping access auditable.

Options include AWS Secrets Manager, Azure Key Vault, Google Secret Manager, HashiCorp Vault, and Doppler or Infisical for smaller teams.

7. Prefer short-lived credentials

The best secret is one that expires before anyone can misuse it.

GitHub Actions OIDC lets a workflow exchange a signed identity token for a cloud access token valid only for that job, so no cloud key is stored in GitHub at all.

A minimal AWS deploy job looks like this:

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: aws-actions/configure-aws-credentials@v6.3.0
        with:
          role-to-assume: arn:aws:iam::123456789012:role/github-deploy
          aws-region: ap-south-1
      - run: aws sts get-caller-identity

The role's trust policy decides which repo, branch, or environment may assume it, as described in the configure-aws-credentials README.


Hands-On Setup: Gitleaks Pre-Commit Hook

This takes about ten minutes and protects every future commit.

1. Install pre-commit

Install the pre-commit framework:

pip install pre-commit

2. Add the config file

Create:

.pre-commit-config.yaml

Add the gitleaks hook from the official README:

repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.30.1
    hooks:
      - id: gitleaks

v8.30.1 was the latest gitleaks release when this guide was written.

3. Update to the latest version

Bump the pinned rev automatically:

pre-commit autoupdate

4. Install the git hook

Wire it into .git/hooks:

pre-commit install

5. Scan what already exists

Run every hook against all files once:

pre-commit run --all-files

6. Test it

Commit a file containing a fake token and you should see:

Detect hardcoded secrets.................................................Failed

The commit is stopped before it exists.

7. Handle false positives properly

For a known test fixture, add a gitleaks:allow comment on that line.

For repos with old findings, create a baseline once and scan against it:

gitleaks git --report-path gitleaks-report.json

Avoid SKIP=gitleaks git commit as a habit; it disables the check entirely for that commit.

8. Add the CI safety net

Hooks live on laptops and can be skipped, so also scan in CI with the official Gitleaks GitHub Action:

name: gitleaks
on: [pull_request, push, workflow_dispatch]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - uses: gitleaks/gitleaks-action@v3
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Organization accounts also need a GITLEAKS_LICENSE secret, per the action's README.


Incident Response Playbook: When a Key Leaks

Speed matters more than elegance.

Work in this order.

1. Revoke or rotate the key immediately

GitHub's guidance is explicit: if the sensitive data is a credential, revoking or rotating it is the first step, and that may be enough on its own.

Deleting the commit first only buys attackers time.

2. Deploy the replacement

Issue a new key with the narrowest scope possible, store it in your secret manager, and redeploy.

3. Audit usage

Check the provider's logs, billing dashboard, and access history for activity between the leak time and the revocation time.

For cloud keys, look for new users, new access keys, unusual regions, and spikes in compute or LLM spend.

4. Decide whether to purge history

If the repo is public, or the data is more than a revocable key, rewrite history with git-filter-repo.

Remove a file from every branch and tag:

git-filter-repo --sensitive-data-removal --invert-paths --path config/.env

Or replace leaked strings listed in a local file (one per line, replaced with ***REMOVED*** by default):

git-filter-repo --sensitive-data-removal --replace-text ../passwords.txt

The --sensitive-data-removal flag needs git-filter-repo 2.47 or newer.

5. Force-push and clean up copies

Push the rewritten history:

git push --force --mirror origin

Then ask collaborators to re-clone, coordinate with fork owners, and contact GitHub Support to remove cached views and pull request references.

6. Prevent the repeat

Add the missing .gitignore rule, install the pre-commit hook, enable push protection, and write a two-line post-mortem.

Flowchart of a leaked API key incident response playbook in three stages: contain by revoking or rotating the key and deploying a scoped replacement, investigate provider logs and billing for abuse, then clean up history with git-filter-repo, force-push, contact GitHub Support, and prevent repeats with .gitignore, a pre-commit hook and push protection

Leaked key playbook: contain, investigate, clean up


Common Mistakes

Mistake 1: Purging history before revoking

A rewrite takes time and coordination, while a live key can be abused within minutes of being pushed.

Mistake 2: Assuming a private repo is safe

Private repos get cloned to laptops, CI runners, and AI tools, and they become public by accident.

Mistake 3: Trusting log masking

Redaction relies on exact matches, so encoded or reformatted values can still appear in logs.

Mistake 4: Hardcoding "temporary" keys

Temporary keys become permanent, and once a key ships inside an app binary or bundle, anyone can extract it.

Mistake 5: One key for everything

A single all-powerful key turns any leak into a full compromise; scope keys per service and environment.


Security Checklist

Before your next release, confirm:

  • .env and key files are in .gitignore, with an .env.example committed

  • gitleaks pre-commit hook installed for every contributor

  • Gitleaks or secret scanning running in CI with full history

  • Push protection enabled on all repositories

  • No secret keys behind VITE_ or NEXT_PUBLIC_ prefixes

  • CI deploys use OIDC instead of stored cloud keys

  • Secrets live in a secret manager with rotation

  • A written leak playbook with owners and provider revoke links


Get Security Alerts in Your Inbox

New breaches, leaked-key incidents, and hands-on fixes land every week.

Subscribe to the free TheTechLane newsletter and get them before they hit your stack.


FAQ

Is deleting the commit enough after leaking a key?

No. The key is still in history, forks, clones, and possibly in scraper caches. Revoke it first, then decide whether to rewrite history.

Does GitHub push protection replace a pre-commit hook?

Not fully. Push protection catches secrets at push time on GitHub, while a local hook stops them before a commit exists and works with any remote.

Should I use gitleaks or TruffleHog?

Many teams use both: gitleaks for fast pre-commit and CI blocking, TruffleHog to verify whether a found credential is still live.

Are environment variables secure enough?

They beat hardcoding, but they are still long-lived and readable by anything in the process or platform. Combine them with a secret manager and short-lived credentials.

What about keys in AI coding tools?

Keep keys out of prompts and agent-readable config, use per-tool scoped keys, and run the same scanners on agent-generated commits.


Key Takeaways

Remember these points:

  • Most leaks come from ordinary workflows: git history, .env files, logs, bundles, and AI context

  • A pre-commit gitleaks hook plus GitHub push protection catches most mistakes before they spread

  • Secret managers and OIDC short-lived credentials shrink what a leak can do

  • When a key leaks: revoke, rotate, audit, then purge history

A leaked key is an access problem first and a git problem second.


Related Reading on TheTechLane


Sources


Follow TheTechLane

Keep learning AI, development, and modern developer technologies with TheTechLane.

Learn • Build • Share

Follow @thetechlanehq for more developer guides, tutorials, and practical technology content.