Key points
- A coding agent can leak any key it can read, so keep broad keys out of its sandbox.
- A credential proxy outside the sandbox adds the key to each request, so the agent never holds it.
- Narrow tokens limit what a leaked key can do, and secret scanning catches keys that reach the code.
How do you keep secrets away from a coding agent?
To keep secrets away from a coding agent, run it in a sandbox that holds none of the user's keys. A credential proxy outside the sandbox adds keys to the agent's API requests. The agent gets its own narrow tokens that expire with the task, and secret scanning checks what it writes.
An agent's commands can read any key its user can read. Coding agents leak secrets by four routes:
- Into the context. The agent reads a
.envfile, so the key goes to the model provider and into the session history. - Into command output. A command, e.g.
env, prints the key into the context and the logs. - Into code. The agent writes the key into a file or browser code. Exposed keys are a common bug in AI-generated code.
- Out to an attacker. Prompt injection tells the agent to send the key to an outside host.
METR disclosed that a dashboard whose authentication silently failed open let an attacker extract an API key and run up about $600,000 in model usage over three weeks. The attacker prompted an agent to reveal its model provider API key. Keeping keys away is one part of making AI-generated code secure.
What do you need before you start?
The setup needs four things:
- A list of the keys on the machine. Keys sit in
.envfiles, shell variables, and files in the home folder, e.g.~/.aws/credentials. - A sandbox for the agent. A container, a dev container, or a virtual machine runs the agent's commands away from the host's files.
- Test keys. Each outside service has a test mode or staging key with no access to production data.
- A way to revoke each key. The team knows how to revoke and replace each one.
How do you keep secrets away from a coding agent step by step?
Here is an illustrative example. Acme Co. sells furniture online. A developer at Acme asks a coding agent to "Let customers edit their delivery address during checkout." The checkout's end-to-end tests call a payment provider's test API with a key.
1. List the keys the agent could reach
On the laptop, the developer lists the names of secret variables, not their values:
$ env | cut -s -d= -f1 | grep -Ei 'key|token|secret'
GH_TOKEN
NPM_TOKEN
PAYMENTS_KEY
An agent started from this shell inherits all three, and ~/.aws/credentials holds a cloud key.
2. Start the agent in a sandbox without them
The developer starts the agent in a dev container with a fresh clone. The .env file is in .gitignore, so the clone has none. The container mounts no home folder, gets no SSH agent or Git credentials from the editor, and sets none of the three variables. The agent's model provider key stays out of its commands' environment.
3. Route the payment API through a credential proxy
A credential proxy runs in its own container. It holds the payment provider's test key and forwards only POST requests to api.payments.example.com. The agent's container sets PAYMENTS_API_URL=http://payments-proxy:8080. It can reach only the proxy, the package registry, the code host, and the model's API, a form of egress control.
4. Give the agent its own code host token
The agent pushes branches with a token limited to acme/web-store that expires after 1 hour, following least privilege.
5. Scan commits before and after they leave
A secret check runs in a pre-commit hook and again in GitHub Actions on each pull request. The second check catches a commit made with --no-verify, which skips the hook.
6. Run the task and read the output
The agent edits the checkout and runs the end-to-end tests, whose payment call goes through the proxy. The address saved, but the cart emptied. While it debugs, the agent prints its payment settings, which hold no key:
$ env | grep PAYMENTS
PAYMENTS_API_URL=http://payments-proxy:8080
The transcript holds the same line. This example is simplified. A real project would route each outside service through a proxy, e.g. the email provider.
What is a credential proxy?
A credential proxy is a service between an agent and an API that attaches the real key to each request, so the key stays out of the agent's environment. A request moves through it in four steps:
- The agent's code sends a request without the key to the proxy.
- The proxy checks the host and can check the path and method.
- The proxy adds the real key, usually in the
Authorizationheader, and forwards the request over HTTPS. - The response returns to the agent's code through the proxy.
HTTPS hides the request from the proxy unless the agent's code calls the proxy's own address, as Acme's does, or trusts the proxy's certificate.
Some coding agents build one in. Claude Code's sandbox can mask an environment variable. Sandboxed commands get a placeholder, and its proxy swaps in the real value only for the hosts in injectHosts:
{
"sandbox": {
"enabled": true, "allowUnsandboxedCommands": false,
"network": {
"tlsTerminate": {},
"allowedDomains": ["api.payments.example.com"]
},
"credentials": {
"envVars": [
{ "name": "PAYMENTS_KEY", "mode": "mask",
"injectHosts": ["api.payments.example.com"] }
]
}
}
}
Claude Code ignores mask entries in a repository's settings files, so the block goes in user settings. A project change cannot redirect the key. Setting allowUnsandboxedCommands to false blocks retries outside the sandbox, where the real key shows.
Four controls cover different gaps:
| Control | What the agent holds | What it stops | What it misses |
|---|---|---|---|
| Credential proxy | No key, or a placeholder | Reading, printing, or committing the key | Misuse of the key through allowed requests |
| Narrow token that expires | A real key with limited access | Use beyond one resource or after it expires | A leak inside its scope and lifetime |
| Secret scanning | Any key it was given | Known key formats in files and commits | Keys no rule matches and files it does not scan |
| Secrets manager | The real key, once a program fetches it | Keys stored in files and code | A leak after a program reads the key |
What are common mistakes?
These mistakes let a key leak or be misused:
- Relying on the agent's deny rules. Claude Code's
Read(./.env)deny rule blocks its file tools and recognized commands, e.g.cat. Its permissions documentation states that it does not cover a script that opens the file itself. - Forgetting the agent's own key. The agent's commands can often read its model provider key. Give it a separate key with a spend alert.
- Putting a key in a browser variable. Next.js copies each
NEXT_PUBLIC_variable that browser code reads into the client JavaScript bundle. When browser code getsundefinedfor a server variable, an agent can add the prefix, which clears the error and publishes the key. - Deleting a leaked key instead of revoking it. Git history keeps the old commit. OWASP's Secrets Management Cheat Sheet orders the response as revocation, rotation, and then deletion.
- Sharing a transcript without scanning it. A transcript keeps whatever a tool returned, including a printed key. Most scans check commits, so scan transcript files before sharing them.
- Putting a broad key behind the proxy. The proxy hides the key, not what it can do. An injected instruction can still send requests through the proxy, so use a test key with narrow access.
How do you check that it worked?
The setup works when these checks pass from inside the sandbox:
- The output of
envshows the proxy's address or a placeholder, and no real key except the agent's own tokens. - Reading a secret file, e.g.
cat ~/.aws/credentials, fails because the file is not there. - A
POSTthrough the proxy to the payment API succeeds, and aGETthrough it fails. - A request straight to the API, without the proxy, fails because egress control blocks it.
- A test commit that holds a fake key fails the pre-commit hook, and the same commit, made with
--no-verify, fails in GitHub Actions.
From the host, search the transcripts and the diff for each real key's value with grep -rF and expect no match. Passing checks cover only the listed keys, not a key that the list missed.
FAQs
Can a coding agent read a .env file?
A coding agent can read a .env file whenever the file sits in a folder its commands can reach. Deny rules in the agent's settings block its file tools and some commands, but a script that opens the file itself can still read it.
What should you do if an agent leaked a key?
After an agent leaks a key, revoke it first, then issue a replacement and update the places that use it. Remove the old key from the code and transcripts last. Deleting the key without revoking it leaves a usable copy in Git history.
Do secret scanners catch keys in agent transcripts?
Secret scanners catch keys in agent transcripts only when someone runs them on the transcript files, because most scans check commits. A scanner looks mainly for known key formats, so a key with no known pattern can pass.
Is a credential proxy the same as a secrets manager?
A credential proxy is not the same as a secrets manager. A secrets manager hands stored keys to the programs allowed to fetch them, so those programs hold the key. A credential proxy uses the key for the agent without handing it over.