Your Secrets Are Already in Version Control
A developer pushed a commit at 10:47 PM on a Friday. It contained an AWS access key. They noticed Monday morning and deleted the file in a follow-up commit. The key had been sitting in a public repository for 60 hours. GitHub has automated scanners that index this kind of thing in minutes. So do the people who hunt for exactly these moments.
The key was rotated Monday. What nobody checked was whether it had been used in the 60 hours between the push and the rotation. The access logs were not reviewed. The incident was closed as resolved.
This story plays out in some variation hundreds of times a day across organizations that have functional security programs, regular penetration tests, and policies that explicitly prohibit committing credentials. The policy exists. The credential is in the repository. Both things are true simultaneously, and they will keep being true until the underlying conditions change.
Where Secrets Actually Live
The git repository is the most visible place. It is not the only one.
Environment variable files are a persistent problem. Files named .env exist specifically to hold credentials, which makes them useful and makes them dangerous when someone adds them to version control despite the gitignore entry that was supposed to prevent it. The gitignore entry does not retroactively clean the history, and .env files have a habit of getting committed once, early in a project, before anyone has thought carefully about what they contain.
CI/CD pipeline logs are a category most organizations underestimate. A debug log that prints environment variables to troubleshoot a failing build. A command that echoes its arguments to the console. A test run that outputs connection strings. These logs often live in your build system's interface, accessible to anyone with repository access, and their contents rarely get audited for credential exposure after the debugging session is over.
Docker images are another place secrets accumulate quietly. A credential used during a build step stays in the image layer even if the file containing it was removed in a subsequent layer. Someone who pulls the image and inspects the layer history has the credential. This is a well-documented problem. It is also consistent across build pipelines that were never designed with this in mind.
Internal messaging platforms complete the list. The Slack channel where someone pasted a database connection string to ask a quick question in 2022 still has that message. It is searchable. The person who pasted it left the company eight months ago.
The Automation That Finds This Before You Do
Secret scanning tools run continuously against public repositories. The major platforms have some version of this built in now. Tools like GitLeaks and TruffleHog can also be run against private repositories and are readily available to anyone who wants to use them.
Attackers automate against these tools. When a new public commit lands, the scan runs. API keys, tokens, and connection strings get extracted, validated against their respective services, and either used or sold within a window that is often shorter than your incident response runbook assumes.
The rotation you do after discovering an exposed credential is the correct move. The problem is the assumption that discovery and use are simultaneous, which is almost never true. The question after any credential exposure is not just whether it was rotated but what was done with it between exposure and rotation, and how you would know.
What Secrets Management Actually Requires
A secrets manager is the foundation. HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, or any equivalent that stores credentials outside of code and provides access through authenticated API calls rather than environment files. The application retrieves the secret at runtime. The secret is not in the repository, the image, or the config file. It is not checked in by accident because it does not exist in the places where accidental check-ins happen.
This requires changing how credentials are provisioned and consumed. That is real work, and it is the reason most teams defer it in favor of the pattern that is faster right now. The faster pattern is why the credentials are in the repository.
Rotation on a schedule, not just on discovery of a leak. A secret that has been sitting in one place for two years is a secret that may already be somewhere you do not know about. Short-lived credentials, where the platform supports them, are better than long-lived ones because their exposure window is bounded by design.
Audit logging on secret access. If a credential is retrieved or used, you want a record of it. This is the mechanism that answers the "what was done with it" question after an incident. It is also the mechanism that surfaces unusual access before anyone has filed a security ticket.
Scanning in the pipeline, not just after the fact. Pre-commit hooks that run secret detection before a commit lands catch the problem at the moment of lowest cost. Developers who get immediate feedback on a potential credential commit fix it in the moment. Developers who find out six weeks later from a security team fix it under pressure while wondering whether anyone found it first.
The Pattern Is Preventable
None of this requires exotic tooling or significant architectural investment for most organizations. A secrets manager, automated scanning in the CI pipeline, rotation policies, and a clean audit of what is currently in your repositories and build logs. The inventory step will be uncomfortable. That discomfort is the starting point.
The credential is already in your codebase somewhere. The question is whether you find it first.