awesome-everything RU
↑ Back to the climb

Security

Secrets management: why a key in git is already a breach

Crux A credential in source code is a leaked credential. Git history keeps it after you delete it, scanners find public keys in about a minute, and the only real fix is rotation, not removal. The maturity ladder runs from hardcoded keys to a managed, audited, short-lived secret.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at junior altitude — the surface
◷ 16 min

A junior pushes a quick fix to a public repo. Buried in the diff is an AWS access key that was “just for testing.” Within a minute an automated scanner watching GitHub’s public event stream has it; within eleven minutes someone spins up a fleet of GPU instances for crypto mining on the company’s account. The dev panics and force-pushes a commit deleting the line. The key still works — it is still live in git history, and rotating it, not deleting it, is the only thing that ends the breach.

A hardcoded credential is a leaked credential

The instinct is to treat a key in source code as untidy — something to clean up before the demo. It is not untidy; it is exposed. The moment a credential lives in a repo, everyone with read access has it, every clone carries it, every CI log can print it, and if the repo is ever made public it is broadcast to the internet. GitHub recorded over 39 million leaked secrets in 2024, a 67% jump year over year, and GitGuardian counted 28.65 million new hardcoded secrets pushed to public repos in 2025 alone. This is not a rare slip — it is the single most common way real systems get breached, which is why OWASP treats secrets management as a first-class concern.

The numbers on exploitation are the part that scares people into changing. Researchers planting honeypot AWS keys saw automated bots find and abuse them in about one minute, and security firms have measured scanners grabbing exposed keys within eleven minutes and immediately launching GPU instances. You do not get a grace period. By the time you notice the bad commit, the attacker has already used the key.

Git history is append-only, so deletion is a lie

The trap that catches most engineers: they git rm the file or delete the line, push, and believe the secret is gone. It is not. Git’s data model is append-only — every commit is preserved, and the secret sits in the history of every clone and fork forever. Anyone who runs git log -p or any scanner that walks full history (and they all do) reads it as easily as the current code.

So the cleanup has two parts, and only one of them actually matters. You can rewrite history with git filter-repo to scrub the blob from every commit — but that does not help anyone who already cloned, and you can never be sure no copy escaped. The step that ends the breach is rotation: revoke the leaked credential at its source so the value an attacker holds becomes worthless. Treat every secret that has ever touched a repo as compromised and rotate it. History rewriting is hygiene; rotation is the fix.

Why this works

Hardcoding is not the only leak path. The same secret can be baked into a Docker image layer (anyone who pulls the image runs docker history and reads it), printed into application logs or a verbose error page, or bundled into a client-side JavaScript build and shipped to every browser. A “backend” secret in a Next.js component that isn’t server-only is one careless import away from the public bundle. Every one of these is the same failure: the secret left the trust boundary.

The maturity ladder: hardcoded → dotenv → managed

There is a clear progression in how teams handle secrets, and most outages happen because a team stalled on a lower rung.

The visual below is the ladder a senior climbs deliberately:

RungWhat it gives youWhat still hurts
Hardcoded in sourceNothing — convenience onlyIn git history forever; leaks on every clone, log, public push
.env + .gitignoreOut of git, out of the image if done rightPlaintext on disk and in CI; lives in process env; no access control or audit; one bad .gitignore from disaster
Secrets manager (Vault, AWS Secrets Manager, SOPS)Encryption at rest, access control, audit log of who read whatOperational cost; still long-lived if you only store static values
Dynamic, short-lived secretsPer-request creds with a TTL, auto-revoked, per-service auditableMost setup; needs an integration the app understands

The big jump is from .env to a secrets manager. A .env file is genuinely better than hardcoding — it keeps the value out of the commit — but it is still plaintext sitting on every developer’s laptop and CI runner, readable by anyone with the box and invisible to any audit. A manager like HashiCorp Vault, AWS Secrets Manager, or SOPS adds the three things a .env can never give you: encryption at rest, access control (this service may read this secret and no other), and an audit trail of every read. When an incident happens, the audit log is what tells you whose credential to rotate and what it touched.

Short-lived beats long-lived: the TTL is the blast radius

The top rung is where senior teams aim, and it changes the math of a leak. A static, long-lived credential is the most common attack vector precisely because it stays valid until a human remembers to rotate it — which, after a leak, is often never. A dynamic secret is generated on demand with a built-in TTL and revoked automatically when it expires. Vault’s database engine, for example, mints a unique database user per request with a one-hour TTL in production; a leaked credential is dead within sixty minutes whether or not anyone noticed.

Two more properties fall out of this. Because each consumer gets a unique credential, the audit log can name the exact service instance that did something — you are not staring at one shared username used by everything. And dynamic creds make least privilege enforceable: each lease is scoped to exactly the permissions that role needs, so a stolen key for the read-only reporting service cannot delete the production database. The senior tradeoff is real — dynamic secrets cost the most to set up and need the app to fetch and renew leases — but for anything protecting production data, a one-hour blast radius beats an indefinite one.

Pick the best fit

A backend service needs database credentials in production. The team can spend a sprint on this. Pick the approach a senior defends.

Quiz

You discover an API key was committed to your repo last month. You delete the line and force-push. Is the secret safe now?

Quiz

Why does a dynamic, short-lived database credential beat a long-lived one for production?

Order the steps

You just found a live cloud key committed to a public repo. Order the response from first to last:

  1. 1 Rotate the credential now — revoke the leaked key at the source so it stops working
  2. 2 Check the provider's audit/billing logs for what the key already did
  3. 3 Rewrite git history (git filter-repo) to scrub the blob from every commit
  4. 4 Add a pre-commit secret scanner (gitleaks/trufflehog) so it can't happen again
  5. 5 Move the secret into a manager with access control and short-lived leases
Recall before you leave
  1. 01
    A teammate committed a database password, then deleted the line and pushed. Explain why that does not fix the problem and what the actual remediation is.
  2. 02
    Walk a teammate up the secrets maturity ladder and explain why each rung is better, ending with why short-lived dynamic secrets are the goal.
Recap

A credential in source code is not a tidiness issue — it is a leaked credential, and leaked keys on public repos are abused in roughly a minute by automated scanners that immediately spin up resources on your account. The defining gotcha is that git history is append-only: deleting the line or even force-pushing does not remove the secret from past commits or from anyone’s clone, so the only thing that ends the exposure is rotation — revoking the value at its source — backed up by checking audit and billing logs for what already happened. From there a senior climbs the maturity ladder: off hardcoding, past a .env file (out of git but still plaintext, uncontrolled, and unaudited), into a secrets manager that adds encryption at rest, access control, and an audit trail, and finally to dynamic, short-lived secrets whose TTL bounds the blast radius of any leak while enforcing least privilege per consumer. The safety net beneath all of it is a pre-commit secret scanner so the next mistake is caught before it ever leaves the machine.

Continue the climb ↑Secrets management: multiple-choice review
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.