GitGuardian scans public GitHub commits every year. Its 2025 report counted 23.8 million new hardcoded secrets pushed in 2024, up 25% on the year before. The finding that worries us more: 70% of the secrets it found back in 2022 still worked when it checked again.

Private repositories aren't a safe place for secrets either. Repos get shared with contractors, cloned onto laptops, mirrored into CI logs and occasionally made public by accident. Once a key lands in git history, deleting the file doesn't remove it.

Where secrets go instead

On every project we separate configuration from secrets:

The application reads secrets at startup from the environment. Developers get their own development keys, never production ones.

The local .env file

Developers need secrets on their machines to run the app. We keep a .env.example in the repo that lists every variable with a fake value, and each developer copies it to .env, which is in .gitignore from the first commit. When someone adds a variable, the PR must update .env.example. That keeps setup instructions honest and stops people from pasting a real key into a committed file "to help the next person".

Two scanners, two places

Before the commit. A pre-commit hook runs a secret scanner (we use gitleaks) on staged changes. It takes under a second and blocks the commit if it finds something that looks like a key.

In CI. The same scanner runs on every pull request, because hooks can be skipped. If CI finds a secret, the build fails and the PR can't merge.

GitHub's own push protection adds a third layer for supported key formats. We turn it on for every organisation we manage.

When a secret leaks anyway

It happens. Someone pastes a key into a support ticket or a CI log prints an environment variable. Our playbook:

  1. Rotate first. Revoke the key and issue a new one before doing anything else. Cleaning git history comes later and doesn't help if the key still works.
  2. Check usage. Look at the provider's logs for calls made with the leaked key since it was exposed.
  3. Find the path. Work out how it leaked and fix that, whether it's a verbose log line or a missing .gitignore entry.
  4. Clean up. Remove it from history if the repo is shared widely, knowing that forks and clones may still hold a copy.

The report's "70% still valid" figure tells you step one is the one teams skip.

Short-lived credentials beat careful handling

The best secret is one that expires before anyone can use it. Where the platform allows it, we use short-lived tokens: CI jobs authenticate to the cloud with OIDC instead of stored keys, and developers use SSO sessions that last hours. You can't leak a password that doesn't exist.

Source: GitGuardian, The State of Secrets Sprawl 2025.

← More in Security