How to Handle Secrets and API Keys

Most leaked credentials are not stolen. They are committed, pasted into a chat, or logged by accident.

How to Handle Secrets and API Keys — Troiana insight cover

In short

Secrets should never be in source control, in client-side code, or in logs. Keep them in environment variables for small projects and a managed secret store as soon as more than one person or environment is involved. Scope every credential to the narrowest permission that works, rotate on a schedule, and treat any leaked key as compromised the moment it is exposed — rotate first, investigate second.

How secrets actually leak

Almost never through a sophisticated attack.

Committed to the repository. Someone adds a config file with a real key. Even after deletion it remains in history, and if the repo is ever made public, it is retrievable. Automated scanners find exposed keys within minutes of a push.

Written into client-side code. Any key shipped to a browser or mobile app is public, however obfuscated. Anyone can open the network tab.

Logged. A debug statement dumping a request, or an error handler logging headers. Logs then get shipped to third-party services and retained.

Pasted. Into a chat, a ticket, a support email, an AI assistant. Each one is a copy you no longer control.

In a screenshot. Terminal output, a config screen, a dashboard.

The common thread: secrets leak through normal work, not through attacks. So the defences have to fit normal work.

Where secrets should live

Environment variables are the baseline. Not in the repository, injected at runtime, different per environment. Good enough for a small project with one or two people.

Their limits appear quickly: no access control, no audit trail, no rotation support, and they get copied into .env files that end up on laptops and in backups.

A managed secret store — your cloud provider's, or a dedicated one — solves those. Access control per service, an audit log of who read what, versioning, and rotation. Worth adopting as soon as more than one person or environment is involved.

Never in the repository, including private ones. Private repositories become public, get forked, and are cloned to machines you do not control.

Never in client-side code. If a browser or app needs to call a third-party service with a secret, it calls your server, and your server holds the key.

Scope every credential

The question for each key is: what is the worst thing this can do if someone else has it?

Least privilege. A key that only reads should only be able to read. Most providers offer scoped keys and most teams use the full-access one because it is easier.

One key per service and environment. Sharing one key across everything means a leak forces you to rotate everything at once, during an incident.

Restrict by origin where offered — IP allowlists, referrer restrictions, domain binding. A key that only works from your servers is far less useful to anyone else.

Set spending limits on anything metered. A leaked key for a paid API is a bill as well as a breach.

Keep them out of the repository

Add .env and equivalents to .gitignore before the first commit, not after. Once committed, the file is in history.

Commit a sample fileconfig.sample.php, .env.example — with the keys named and the values blank. New developers get the shape without the secrets, and nobody invents their own naming.

Run a secret scanner in CI, and as a pre-commit hook. Catching a key before it is pushed is a minor inconvenience; catching it afterwards is an incident.

Exclude secrets from deploys explicitly. If your deployment copies files, make sure config files holding real credentials are on the exclusion list, so a deploy cannot overwrite server-side secrets or ship local ones.

Keep them out of logs

Never log request headers wholesale. Authorization headers are the obvious casualty.

Redact known field namespassword, token, secret, key, authorization — in your logging layer, so it happens by default rather than by discipline.

Be careful with error reporting. Exception trackers often capture local variables and request context. Configure the redaction rules before you need them.

When a key leaks

Speed matters more than diagnosis.

Rotate first. Issue a new credential, deploy it, revoke the old one. Do not wait to determine whether it was actually used — the investigation can happen against a revoked key.

Then check usage. Most providers expose logs. Look for calls from unfamiliar addresses or unusual volume in the exposure window.

Assume the whole history is exposed if it was committed. Removing the file does not remove it from history; rewriting history does not help if anyone cloned or forked.

Write down what happened. Which key, exposed how, for how long, what it could reach, what you rotated. This is the record that prevents the same route being used again — and it is what a client will reasonably ask for.

The habits that prevent recurrence

Rotate on a schedule, not only after incidents — it proves rotation actually works before you need it under pressure. Review who has access to production secrets when people join and leave. Keep development and production credentials entirely separate, so a development mistake cannot touch live data. And make the sample-config file part of onboarding, so the correct pattern is the easiest one to follow.

If you are inheriting a codebase and are not sure what is exposed, book a call — a scan of the history is usually the first thing worth doing.

Common questions

Where should API keys be stored?

In environment variables for a small project, and in a managed secret store as soon as more than one person or environment is involved — because environment variables offer no access control, audit trail, or rotation support. Never in the repository, including private ones, and never in client-side code.

Is it safe to keep secrets in a private repository?

No. Private repositories get made public, forked, and cloned to machines you do not control, and anything committed remains in history after the file is deleted. Automated scanners find exposed keys within minutes of a push becoming public.

What should I do if an API key is leaked?

Rotate immediately — issue a new credential, deploy it, revoke the old one — before investigating whether it was used. Then check the provider's usage logs for unfamiliar addresses or unusual volume during the exposure window, and record what happened, what it could reach, and what you rotated.

Can I hide an API key in a mobile app or front-end code?

No. Any key shipped to a browser or mobile app is public regardless of obfuscation, since anyone can inspect network traffic or decompile the bundle. If a client needs to call a service that requires a secret, route the call through your own server and keep the key there.

How do I stop secrets ending up in logs?

Redact known field names such as password, token, secret, key and authorization in the logging layer so it happens by default rather than relying on discipline, and never log request headers wholesale. Configure the same redaction in your exception tracker, which often captures local variables and request context automatically.

Have something worth building right?