Stop your AI assistant leaking secrets
Coding assistants hardcode keys because their training data is full of them. Keep .env out of context, block secrets with hooks before they commit, and rotate anything already exposed.
AI assistants leak secrets because they optimize for code that works right now, and the fastest way to make an API call work in an example is to paste the key inline. They also learned from public repositories full of hardcoded credentials, so an inline token looks normal to them. The fix is three layers: keep secret files out of the agent's context, block secrets with a hook before they ever reach a commit, and rotate anything that already leaked. None of it is exotic, and the first two are a few lines of configuration.
Why AI assistants hardcode secrets
It is worth understanding the mechanism, because it tells you where to intervene:
- They pattern-match to their training data, which is full of public code with inline keys and tokens — so a hardcoded credential reads as the idiomatic way to authenticate.
- They optimize for the working demo, not the deployment. A literal value makes the snippet run on the first try; wiring up a secret manager does not.
- They lack your runtime context. The assistant does not know your secret lives in a vault or an environment variable unless the surrounding code makes that obvious.
- They will echo real values back. If a key is anywhere in the context window — a pasted log, an open
.env, a prior message — the model may reproduce it verbatim in generated code or a commit message.
What the data shows
According to GitGuardian's research, this is not hypothetical. GitGuardian reported that repositories with GitHub Copilot enabled leaked secrets at a 6.4% rate versus a 4.6% baseline — assistants correlate with more exposed credentials, not fewer. And the underlying problem is large and growing: GitGuardian's State of Secrets Sprawl reports counted roughly 23.8 million secrets exposed on public GitHub in 2024, rising to about 29 million in 2025. These are vendor-published figures, so read them as GitGuardian's measurements rather than a universal constant — but the trend and the direction are clear.
Where secrets actually leak
Hardcoded keys in application source are the obvious case, but with an AI assistant in the loop the exposure surface is wider. It helps to know the less-obvious paths before you defend them:
- Generated test fixtures and example files, where a realistic-looking token is often a real one the model saw earlier in the session.
- Commit messages and PR descriptions the assistant drafts, which can quote a value it just wrote into the diff.
- Config and infrastructure files —
docker-compose.yml, CI YAML, Terraform — where inline credentials read as normal to the model. - Anything you paste into context: a log, a stack trace, an open
.env. Once a secret is in the window, the model may reproduce it verbatim somewhere you did not expect.
Keep .env out of the agent's context
The cheapest win is preventing the assistant from ever reading your secrets. Claude Code lets you deny file reads in settings.json, and you should combine that with a .gitignore so the files never enter version control in the first place. The assistant cannot leak what it cannot see.
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./**/*.pem)",
"Read(./**/secrets/**)"
]
}
}.env
.env.*
*.pem
*.key
**/secrets/Block secrets before they commit
Prevention in context is not enough, because the assistant can still write a literal key into source code it generates. Two hooks close that gap. The first is a Claude Code PreToolUse hook that inspects every Write and Edit before it lands on disk and rejects anything that looks like a credential. Configure it in settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": ".claude/hooks/block-secrets.sh" }
]
}
]
}
}The hook receives the tool call as JSON on stdin. Exiting with code 2 blocks the tool and feeds the message back to Claude, so the assistant sees why it was stopped and can correct course:
#!/usr/bin/env bash
# .claude/hooks/block-secrets.sh
# PreToolUse: reject writes that contain a likely secret.
input="$(cat)"
patterns='(AKIA[0-9A-Z]{16}|sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{36}|-----BEGIN [A-Z ]*PRIVATE KEY-----|xox[baprs]-[A-Za-z0-9-]+)'
if printf '%s' "$input" | grep -qE "$patterns"; then
echo "Blocked: this write appears to contain a hardcoded secret. Use an environment variable or your secret manager instead." >&2
exit 2
fi
exit 0The second is a plain git pre-commit hook — the same idea, one step later, catching anything that slips past the agent (including edits you make by hand). It scans the staged diff and refuses the commit:
#!/usr/bin/env bash
# .git/hooks/pre-commit — last line before a secret is committed.
patterns='(AKIA[0-9A-Z]{16}|sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{36}|-----BEGIN [A-Z ]*PRIVATE KEY-----)'
if git diff --cached -U0 | grep -nE "$patterns"; then
echo "Commit blocked: staged changes contain a likely secret. Remove it and rotate the key."
exit 1
fiRotate what already leaked
If a secret has been committed and pushed, prevention is too late — remediation is the job. Do these in order:
- Treat it as compromised. Do not reason about whether anyone saw it. Assume they did.
- Revoke and reissue at the provider. Rotate the key or token at its source; deleting the line in code does nothing to the live credential.
- Check for abuse. Review the provider's audit logs for calls made with the leaked credential, and revoke any sessions it created.
- Then purge from history if you must — but rotation comes first. Rewriting git history without rotating is theatre; the copies are already out.
The mistake to avoid
The mistake is treating this as the assistant's job to get right. It will not — hardcoding keys is the path of least resistance for a model optimizing for a working snippet. Move the responsibility to the environment: deny the reads, run the hooks, and scan in CI, so a leaked secret is stopped by the system rather than by someone remembering. Claude Code gives you the deny-list and the PreToolUse hook for free, and you should use them; the Kaairos Security Kit ships maintained, license-audited versions of these hooks alongside secret scanning across the toolchain, so the tripwire stays current as credential formats change. For the wider defensive picture, the AI coding agent guardrails checklist puts secret handling in context, and reviewing for hardcoded credentials is one line on the secure review workflow.