It's 11:48 PM. Production is throwing HTTP 500 errors. 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" ends up in the main branch.
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, using the same open-source tools defenders rely on, 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 snapshot. The deletion commit records the absence of the file; it does not remove the commit that added it.
Actually removing it means rewriting the entire git history. That is a disruptive process.
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 (Identity and Access Management) 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 the initial discovery. This gap makes it harder to link the attack back to the original leak.
What to do immediately
-
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.
-
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.
-
Rewrite the git history to remove the commit containing the secret. Do this after rotation.
-
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-secretsandgitleaksrun locally before a commit is created. A.pre-commit-config.yamldeploys 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.
Git is designed to never forget. Deleting a secret removes it from the latest snapshot, not from history. A committed secret stays there until you rewrite history deliberately. That cleanup is slow and disruptive. Keep that cost in mind before reaching for git add . at 11:48 PM.
Lumstep's secret detection scanner checks 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.
Or let Lumstep handle it.
Connect a repo and Lumstep scans it automatically - secrets, dependencies, SBOM, and code quality - and opens the fix PR.