Leaked credentials in public repos get used in minutes
GitGuardian’s 2026 State of Secrets Sprawl report counted 28.65 million new hardcoded secrets pushed to public GitHub commits in 2025 alone — a 34% jump year over year and the largest single-year increase they’ve recorded (GitGuardian). That’s not a backlog of old mistakes; it’s this year’s output. And the same report found that 64% of secrets that leaked back in 2022 are *still valid* today (GitGuardian). Meanwhile Unit 42’s tracking of the EleKtra-Leak campaign found attackers detecting and using exposed AWS IAM credentials within roughly five minutes of them landing on GitHub (Unit 42). Committing a secret to a public repo isn’t a “someone might find this eventually” risk. It’s closer to broadcasting it live.
Root cause
Almost none of this is malicious. It’s .env files added to a repo before
anyone wrote a .gitignore entry for them, database URLs hardcoded into a
config “just for local testing,” service-account JSON dropped into a test
fixture, or a CI job that echoes an environment variable into build logs
that are themselves public. GitGuardian’s report also flags AI-assisted
coding as a growing contributor — commits with AI-generated code leak
secrets at roughly double the baseline rate, and secrets tied to AI
services specifically were up 81% year over year (GitGuardian),
which tracks: an assistant that’s never seen your .gitignore will happily
paste a working API key straight into a code sample.
The part engineers consistently underestimate is that deleting the secret in a follow-up commit doesn’t delete it from the repo. Git preserves history — the credential is still sitting in an earlier commit object, reachable by anyone who clones the repo or even just browses the commit log on GitHub. Force-pushing a rewritten history to your own branch doesn’t help either if the repo was ever public even briefly: forks, local clones, and GitHub’s own caches can retain the blob indefinitely. The only thing that actually neutralizes a leaked secret is invalidating the credential itself — history rewrites are cleanup, not remediation.
Blast radius
This is the part that should change how you triage. Automated scanners watch GitHub’s public event stream continuously, and multiple independent research efforts have clocked exploitation in the single-digit minutes: Unit 42 measured attackers weaponizing exposed IAM keys from the EleKtra-Leak campaign in about five minutes (Unit 42), and independent write-ups of live incidents describe leaked AWS keys being grabbed and turned into GPU cryptomining fleets within 4 to 11 minutes of the push, run across every region the credential could reach (TechRadar). The economics are simple: spinning up GPU instances for Monero mining costs the attacker nothing (it’s your bill) and the scanning infrastructure is fully automated, so there’s no human in the loop slowing things down. There is no “we’ll rotate it during the next sprint” window here — by the time a human notices the commit, the credential has often already been used.
Remediation
The controls that matter, roughly in order of leverage:
- Push protection. GitHub’s secret scanning can block a push before a known-format secret ever lands in the repo, and its partner program automatically notifies the issuing provider (AWS, Stripe, and ~150 others) when a valid secret is detected in a public repo, sometimes triggering revocation faster than the committer even sees the alert (GitHub Docs). Turn this on org-wide, not per-repo.
- Pre-commit scanning. Tools like
gitleaksortrufflehogrun locally before a commit is even made, catching secrets in formats GitHub doesn’t recognize (internal API keys, custom tokens) and catching them before they touch history at all, which is strictly better than catching them after. - Treat any committed secret as compromised, full stop. Don’t reach
for
git filter-branchor a force-push as the first response — rotate the credential at the source first. A scrubbed history with a still-valid key behind it protects nobody; an invalidated key with a messy history is a Tuesday. - Shrink the blast radius with short-lived credentials. The strongest structural fix is having fewer long-lived secrets to leak in the first place. This site’s own CI/CD (see the GitHub Actions OIDC to AWS tutorial) uses OpenID Connect federation instead of storing AWS access keys as repo secrets — GitHub Actions requests a short-lived, auto-expiring token scoped to a specific IAM role for each run, so there’s no long-lived AWS key sitting in secrets storage to leak in the first place. Where OIDC isn’t an option, aggressive credential expiry and scoped-down IAM policies get you most of the same benefit.
The bigger lesson
Prevention controls — push protection, pre-commit hooks, .gitignore
discipline — are worth having, but GitGuardian’s own numbers show they
aren’t closing the gap: secrets leaked have grown 152% since 2021 against
a 98% growth in GitHub’s developer base (GitGuardian),
meaning the leak rate is outpacing the population producing the leaks.
Some secret will eventually get past whatever scanner you’ve configured,
because prevention is a filter and filters have gaps by definition. What
actually determines whether that leak becomes an incident report or a
non-event is how fast you can rotate the credential and how little damage
it can do in the window before you notice. That’s why the real investment
isn’t just “stop secrets from leaking” — it’s “assume one will leak, and
make sure it’s short-lived, narrowly scoped, and rotated before a bot ever
gets a chance to use it.”
Join the discussion
Comments for this post live on social — reply to the thread.
Related posts
Cloud roundup: macOS Screen Sharing bug now under attack
A patched macOS Screen Sharing flaw is being exploited to plant crypto miners, a Windows Defender bypass has no fix yet, and EC2 gets built-in app health checks.
Cloud roundup: S3 finally names the policy that denied you
AWS S3 access-denied errors now name the exact policy ARN, Client VPN gets a scriptable CLI, and OpenAI ships authorized offensive-security models on Bedrock.
The Capital One breach: an SSRF bug into 100M records
A misconfigured WAF and an SSRF bug let an attacker reach the AWS metadata service and steal role credentials — exposing 100M+ Capital One records.