Environment variables and secrets: where they should never be
The places we find API keys and passwords during audits, why each is dangerous, and the one place secrets belong.
The short answer
A secret is any value that grants access: an API key for the email service, a database password, a token for the automation platform, a signing key for webhooks. Wherever a secret is stored, whoever can read that place holds that access, which is why where secrets live matters more than how long or random they are. During audits of inherited sites we find secrets in repositories, current and historical, in front-end code every visitor downloads, in build and deployment logs, in chat messages, in shared documents and spreadsheets, in screenshots and in the environment files developers keep on laptops. Each is a leak waiting to be found, and several are found routinely by automated scanners. Secrets belong in one place: the platform’s secret storage, injected into the running function or the build, scoped per environment, with access limited to named people and never printed. A secret found anywhere else is revoked and replaced immediately, and the place it leaked from is fixed.
Where we find secrets and why each is dangerous
| Where | Why it is there | Why it is dangerous |
|---|---|---|
| Committed in the repository | Convenience during development | Every clone, fork, backup and past collaborator has it; history keeps it after removal |
| In front-end JavaScript | The developer called the API from the browser | Every visitor downloads it; it is public |
| In build or deployment logs | A script printed configuration | Logs are kept, shared and rarely protected |
| In chat messages and email | Shared quickly between colleagues | Searchable forever, in accounts that get compromised |
| In shared documents and spreadsheets | The team’s password list | Wide access, no audit, copied everywhere |
| In screenshots | Documentation or a support ticket | Stored in image libraries and tickets indefinitely |
| In environment files on laptops | Local development | Lost or stolen devices; synced to cloud folders |
| In a content system’s settings pages | A plugin wanted a key | Readable by every admin; often exposed by plugin flaws |
| In error messages on the site | Verbose errors in production | Shown to whoever triggers the error |
The rules
- Every secret lives in the platform’s secret storage, per environment, never anywhere else.
- Front-end code holds only values designed to be public; everything else is called through a function.
- Repositories contain an example environment file with names and no values; real environment files are ignored by version control.
- Nothing prints secrets: logs, error messages and build output are checked.
- One key per integration, with the narrowest permissions the provider allows, so a leak is contained.
- Access to secret storage is limited to named people; leavers are removed the same day.
- Scanning is on: the repository is checked for secret patterns on every change.
- A leak means revoke and replace now, then fix the source, then check the provider’s usage logs.
Public keys and real keys
Some values are designed for the browser: a payment provider’s publishable key, an analytics measurement identifier, a map key restricted to your domain. They can be in front-end code because they cannot do harm on their own or are restricted to your site. The test is what the value can do if a stranger has it. If it can send, charge, read, write or delete, it is a real secret and belongs in secret storage behind a function.
What this means for you
Secrets grant access to whoever can read where they are kept. Keep every one in the platform’s secret storage, per environment, with named access; keep front-end code free of anything that can act; keep repositories, logs, chats, documents and screenshots free of them; scope each key narrowly; scan continuously; and treat any secret found elsewhere as leaked and replace it. The discipline costs nothing and removes the most common way a small business hands its accounts to strangers.
Frequently asked questions
The key is in a private repository. Is that not safe?
No. Private repositories are cloned to every developer's laptop, backed up, forked, occasionally made public by mistake and accessible to everyone who ever had access. A key committed once stays in the history even after it is removed from the current version. Repositories hold code; secrets hold access; they must not share a home.
What about a key in the website's JavaScript?
Anything in front-end code is public: every visitor's browser downloads it and anyone can read it in a few clicks. A key there is a key published on the internet. Some keys are designed to be public, such as a payment provider's publishable key or an analytics identifier, and those are fine. Anything that can charge, send, read or write is not, and belongs behind a function that holds the real key.
How do we know if a secret has already leaked?
Search the repository history for key patterns, check build logs, review chat and document search for the strings, and use the provider's dashboard to see where a key has been used from. Most providers now scan public repositories and revoke keys they find, which is a sign yours was exposed. When in doubt, revoke and replace; it costs minutes, and a leaked key costs a great deal more.
Sources
- OWASP Cheat Sheet Series: Secrets Management Cheat Sheet (accessed 2026-09-12)