What Happened
Truffle Security's research revealed that over 543,000 valid credentials were exposed in public GitHub repositories as of July 2024. These weren't just outdated tokens from abandoned projects. They were active credentials that could grant immediate access to production systems, databases, and APIs.
This exposure persisted despite GitHub enabling Push Protection by default in May 2023. After February 2024, 199,843 new credentials appeared in public repositories. The median credential stayed publicly accessible for 784 days before anyone noticed or revoked it. Some credentials dated back to 2009, sitting exposed for over a decade.
Timeline
2009-2023: Credentials accumulated in public repositories, many remaining valid for years without detection or rotation.
May 2023: GitHub enabled Push Protection by default across all public repositories to block secret commits at push time.
February 2024: Push Protection coverage expanded, yet new credential exposures continued.
July 2024: Truffle Security's scan identified 543,000+ valid credentials still accessible in public repositories, including 199,843 exposed after February 2024.
Which Controls Failed
The fundamental failure wasn't GitHub's Push Protection. It's a pattern-matching system that scans commits for known secret formats. Within its detection scope, it works. The problem is what falls outside that scope.
Missing credential lifecycle management: Organizations treated credential creation as a one-time event. No expiration dates. No rotation schedules. No automated revocation. Once issued, credentials lived indefinitely until someone manually deleted them.
No monitoring for public exposure: Teams didn't scan their own public repositories for accidentally committed secrets. They relied entirely on GitHub's detection, which only triggers during new pushes. Historical commits stayed unscanned.
Inadequate secret rotation policies: The 784-day median exposure time reveals that most organizations don't rotate credentials on any regular schedule. Even after a credential appeared in a public commit, it remained valid for over two years on average.
No automated secret detection in CI/CD: Pre-commit hooks and CI pipeline scans could catch secrets before they reach GitHub. Most teams don't run these checks.
Missing break-glass procedures: When a credential does leak, you need a documented process to revoke it within minutes, not days. The 784-day median suggests these procedures either don't exist or aren't enforced.
What the Standards Require
PCI DSS v4.0.1 Requirement 8.3.9 mandates that authentication credentials are changed if there's suspicion of compromise. A public GitHub commit counts as confirmed compromise, not mere suspicion. The 784-day exposure window violates this requirement by roughly 783 days.
NIST 800-53 Rev 5 Control IA-5(7) requires organizations to ensure authenticators have sufficient strength for their intended use and are protected against unauthorized disclosure. Credentials sitting in public repositories for years fail both criteria.
ISO/IEC 27001:2022 Control 5.17 requires that allocation and management of authentication information be controlled through a formal process. That process must include handling compromised credentials. If your process allows credentials to remain valid for 784 days after public exposure, you don't have a formal process.
SOC 2 Type II CC6.1 requires that the entity implements logical access security measures to protect against threats from sources outside its system boundaries. A credential in a public GitHub repository is outside your system boundaries and accessible to any threat actor with a browser.
Lessons and Action Items
Implement automated secret scanning across your entire GitHub organization. Don't wait for Push Protection to catch secrets at commit time. Tools like TruffleHog, GitGuardian, or GitHub's own secret scanning API can audit your existing repositories. Run this scan weekly, not once.
Establish 90-day maximum credential lifetimes. Rotate API keys, database passwords, and service account credentials every 90 days regardless of suspected compromise. For high-privilege credentials, rotate every 30 days. This limits exposure windows from years to months.
Deploy pre-commit hooks that scan for secrets locally. Tools like git-secrets or detect-secrets run on developer workstations before code reaches GitHub. They catch secrets in .env files, configuration files, and hardcoded strings before they become public.
Create a credential revocation runbook. Document exactly who revokes what, in what order, within what timeframe. Test it quarterly. Your target: revoke and rotate any exposed credential within one hour of detection.
Use secret managers, not environment variables. HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault generate short-lived credentials that expire automatically. You can't leak a credential that stops working in four hours.
Enable GitHub Advanced Security for private repositories. Secret scanning isn't just for public repos. Private repository commits leak too when contractors leave, laptops get stolen, or repositories accidentally flip to public.
Monitor for your credentials in public data. Services exist that continuously scan public GitHub repositories, paste sites, and other exposure vectors for your organization's credential patterns. Subscribe to one. Waiting for a breach notification means you're already compromised.
The 543,000 exposed credentials aren't a GitHub problem. They're a credential lifecycle problem. Push Protection blocks some new leaks, but it doesn't fix the credentials you leaked last year, or five years ago, that still work today. Fix that.





