Guide · beginner

The Anatomy of a Credential Leak: From Forgotten API Key to Breach

How a hardcoded API key goes from a rushed hotfix commit to an attacker's toolkit — and what the realistic timeline looks like between push and exploitation.

LT
Équipe Lumstep
Ingénierie sécurité
6 minUpdated juin 2026

It's 11:48 PM. Production is throwing 500s. You've found the bug and you know the fix. You commit, push. The incident is over.

In that commit, three lines above the fix, is this:

STRIPE_SECRET_KEY = "sk_live_4eC39HqLyjWDarjtT7BdA9Rm"

You didn't notice. Within 47 seconds, an automated bot had read it.

How credentials end up in commits

There are three common paths.

Direct hardcoding. A secret added during development — typically while testing something quickly — that never gets removed before commit. The "placeholder" ships to main.

Committed .env files. .env files are designed to hold secrets and to be excluded via .gitignore. When the exclusion is missing or the project is new, the entire file lands in the repository: database URLs, API keys, tokens.

Config files committed alongside code. docker-compose.yml with hardcoded database credentials, Kubernetes manifests with inline secrets, CI pipeline configs with embedded tokens. These look like configuration, not secrets, and travel with the code.

What happens in the first 60 seconds

Public GitHub repositories are watched continuously by credential-scanning bots. These subscribe to the GitHub Events API and parse every commit diff for patterns matching known secret formats — the same open-source tools defenders use, pointed at incoming commits.

The numbers are measured: 56% of exposed secrets are accessed within four seconds of being pushed (NC State, 2019). GitGuardian's 2024 data logged detection-to-use events as fast as 47 seconds for Stripe live keys and AWS IAM keys.

If a high-value credential lands in a public repository, assume it has been seen within a minute. Private repositories reduce this risk but don't eliminate it — repository visibility changes, source code leaks happen, and contributors (current and former) all have access. The commit history is the persistent record of every mistake, and that record travels with the repository.

Why git history is the real problem

The intuitive response to committing a secret is to delete it. But the secret is not gone — it is in the git history. Every commit is a permanent, content-addressed object. The deletion commit records the absence of the file; it does not remove the commit that added it.

Actual removal requires rewriting history with BFG Repo Cleaner or git filter-branch, followed by a force-push. This rewrites the object store and requires all contributors to re-clone. It is cleanup, not the primary remediation.

Treat credential rotation as the mandatory first step. History rewrite is secondary cleanup.

What attackers do with found credentials

High-confidence credentials are tested automatically by the same tooling that detects them. An AWS key can be validated with one API call in seconds. A valid response returns the account ID and IAM permissions.

Cryptocurrency mining is the fastest path to monetization for compute credentials. Attackers spin up the largest GPU instances in every region. A 2023 Sysdig report estimated a single compromised cloud account generates $53 of mining value for every $430 charged to the victim.

Data exfiltration targets S3 buckets, database dumps, and customer PII. One GitHub token with repository access is not just a repository credential — repos contain other credentials, deployment configs, and cloud keys.

Delayed use is common. The majority of malicious use happens days or weeks after initial discovery, decoupled from the exposure event to make forensic correlation harder.

What to do immediately

1. Rotate the credential first. Generate a new secret, update your configuration with the new value, revoke the old one. In that order — revocation without a replacement takes down your application.

2. Check cloud provider logs. AWS CloudTrail, GCP Audit Logs, and Azure Activity Log record all API calls. Search for calls using the compromised credential since the commit. Look for resource creation, IAM modifications, and data access from unfamiliar regions.

3. Rewrite the git history to remove the commit containing the secret. Do this after rotation.

4. Audit for persistence. If you found unauthorized API calls, check for IAM users, OAuth applications, S3 policies, or webhook endpoints the attacker may have created. Credential rotation does not revoke access granted through these.

Prevention

Pre-commit hooks. Tools like detect-secrets and gitleaks run locally before a commit is created. A .pre-commit-config.yaml deploys them project-wide in minutes.

Use a secrets manager. AWS Secrets Manager, HashiCorp Vault, and similar tools keep secrets out of code and environment files. The marginal cost is low.

Rotate credentials on a schedule. Short-lived credentials that rotate automatically limit the value of any stolen credential. A 24-hour token that has already expired when an attacker tries to use it is worthless.

The underlying lesson is about git's design. Git is built to preserve history perfectly. Deletion feels like removal. It isn't. The history is a ledger, not a mutable document. A committed secret is a fact in the repository's historical record, and erasing it requires deliberate, disruptive action — an asymmetry worth holding in mind every time you reach for git add . at 11:48 PM.


Lumstep's secret detection scanner runs Kingfisher against both the working tree and the full git history of every repository — secrets that have been "deleted" but remain in history are surfaced alongside active findings.

Ou laissez Lumstep s'en charger.

Connectez un repo et Lumstep le scanne automatiquement - secrets, dépendances, SBOM et qualité du code - et ouvre la PR de correction.

Accès anticipé

Continuer la lecture