Ask any security researcher where they start when poking at a small app and the answer is boring: they search the code and the shipped bundle for things that look like secrets. It works far more often than it should.
What is a hardcoded secret and why is it dangerous?
A hardcoded secret is an API key, token, password, or connection string written directly into source code instead of loaded from the environment. It is dangerous because code travels: into public repositories, shipped bundles, and git history. Anyone who reads any of those reads the credential, and it stays valid until you rotate it.
Why hardcoded secrets are so common
You are moving fast. You paste a Stripe key or an OpenAI token inline to make something work, promise yourself you will move it to an environment variable later, and later never comes. Then the repo goes public, or the key ships inside your JavaScript bundle, and it is live on the internet.
The scale of this is genuinely hard to believe until you see the numbers. GitGuardian's State of Secrets Sprawl 2026 found 28.65 million new hardcoded secrets pushed to public GitHub during 2025, up 34% year over year and the largest single-year jump they have recorded. Secrets tied to AI services alone hit 1.27 million, an 81% rise, and commits written with AI assistance leaked secrets at roughly double the baseline rate. If your workflow is "describe the integration, paste the result, ship it", that last statistic is about you. The vibe coding security post covers the wider pattern.
Where they hide
- Inline in source files, especially config and API-client setup.
- In the production build output (the bundle), even when the source uses an env var incorrectly.
- In git history, committed once and "removed" later but still recoverable. This one deserves its own post, because deleting the line and committing the deletion does nothing.
- In
.envfiles that got committed by accident. - In config that is not code: CI variables, deploy scripts, and infrastructure templates. GitGuardian put about 28% of secrets sprawl outside code repositories entirely, in collaboration and productivity tooling. See insecure infrastructure-as-code defaults.
Worth knowing which of these your tooling already covers. GitHub secret scanning and push protection are free on public repositories and on by default, so a recognised key gets blocked at push time. Private and internal repositories need the paid GitHub Secret Protection product, which matters because internal repos are where the problem concentrates: 32.2% of them contain at least one hardcoded secret, against 5.6% of public repos. People are careful in public and careless behind the login.
Pattern matching is not enough
Known prefixes (like sk_live_ for Stripe) are easy to flag. The dangerous ones are the generic high-entropy strings: a random 40-character token with no recognizable prefix. Catching those needs entropy analysis, not just a keyword list.
If a secret ever touched your code, assume it is compromised and rotate it. Removing it from the current file is not enough.
Getting them out
Move every secret to an environment variable, add .env to .gitignore, and rotate anything that was ever committed. Then scan the built bundle, not just the source, because that is what actually ships to users. A good secret scan checks both, and flags the high-entropy strings a keyword list would miss.
Rotation is the step people skip, and it is the only one that actually closes the hole. Everything else stops the next leak. Rotating invalidates the key that already leaked. Do it in that order: rotate first, then clean up the code, because the cleanup takes longer and the key is live the whole time. Then make the scan part of your release rather than a thing you remember to do, which is the argument in the pre-launch security checklist and the pre-ship audit walkthrough.