Your AI Assistant Just Committed a Secret. Here's Why That's Your Fault.
作者:Seaworthy · 2026年10月24日
Photo by Kadarius Seegars on Unsplash
I keep seeing the same pattern in code reviews: a function that calls an external API with a key pasted right into the source. Sometimes it's an OpenAI token. Sometimes it's a database password. Sometimes it's a Stripe secret key sitting in a React component like a house key taped to the doorknob. And more often than not, the developer says the same thing: "The AI wrote it that way."
That excuse needs to die. Your AI coding assistant is a pattern-completing machine. It has read a lot of tutorials, and a shocking number of those tutorials hardcode credentials because it makes the example short. When you ask for a quick integration, the model gives you the shortest path to working code, and the shortest path often includes a literal string where a secret should never be.
This is not a subtle failure. It is the most basic security mistake in the book, and we are watching a new generation of developers make it at scale because the tooling makes it feel normal.
The pattern is embarrassingly simple
Here is what shows up in pull requests constantly:
// lib/weather.js
const API_KEY = "sk_live_51H8x...9fQ2";
export async function getForecast(city) {
const res = await fetch(
`https://api.example.com/v1/forecast?city=${city}&key=${API_KEY}`
);
return res.json();
}
It works. That is the problem. It works locally, it works in CI, it works in production, right up until someone scrapes your public repo or your bundled frontend JavaScript and starts burning your quota or your cloud bill. Then it stops working very fast.
And if you pushed that key to a public repository, rotating it is not optional. Assume it is compromised the moment it touches a remote. Git history keeps it forever unless you rewrite history, and even then, forks and caches may hold copies.
The blast radius depends on what the key can do. A read-only token for a weather API is annoying to lose. A database password with write access is a full incident. A cloud provider key with broad permissions can spin up expensive infrastructure in minutes, and by the time you notice the bill, the attacker has moved on. Treat every leaked credential as if it has the worst permissions you can imagine, because you probably have not audited what it can actually reach.
The fix is boring on purpose
The correct version of that file looks like this:
// lib/weather.js
const API_KEY = process.env.WEATHER_API_KEY;
if (!API_KEY) {
throw new Error("WEATHER_API_KEY is not set");
}
export async function getForecast(city) {
const res = await fetch(
`https://api.example.com/v1/forecast?city=${city}&key=${API_KEY}`
);
return res.json();
}
Then you put WEATHER_API_KEY=... in a local .env file, add .env to .gitignore, and set the real value in your deployment platform's secret manager. That is it. No vault product required for a small project. No ceremony. Just stop writing secrets in source files.
For frontend code, the rule is stricter: anything shipped to the browser is public. If your AI assistant writes a secret key into a client-side bundle, it has handed that key to every visitor. Server-side proxies exist for a reason.
A common follow-up question is what to do about the credential that already leaked. The answer is a short checklist. Revoke it in the provider dashboard first, before you write a single line of cleanup code. Then issue a replacement, update the environment configuration, and redeploy. Only after the old key is dead should you worry about scrubbing history, because a cleaned-up history with a live key in it protects nobody. If the key was in a published package or a public repo for any length of time, check provider logs for usage you do not recognize and rotate anything else that key could reach.
Make the assistant earn its keep
You can improve your odds without much effort. Put a short instruction in your project's rules file (.cursorrules, AGENTS.md, whatever your tool reads) that says: never hardcode credentials, always read from environment variables, never commit .env files. Most assistants follow that instruction most of the time. It is not a guarantee, but it shifts the default.
Add a pre-commit hook that scans for high-entropy strings and known key prefixes. There are several free scanners that run in under a second. If a hook blocks your commit, that is the system working.
Pair that hook with a CI check that runs the same scanner on every pull request, so nothing slips through when someone commits with hooks disabled. The two layers cost almost nothing and catch different mistakes: the hook stops the lazy commit, the CI check stops the determined one. Add a short note in your contributing guide telling people what to do when the scanner fires, because a blocked commit with no instructions turns into a bypassed commit.
And review AI-generated diffs with the same suspicion you would apply to a stranger's pull request. The model does not know your threat model. It does not know that this key has production write access. It just knows the example looked like this.
My actual opinion
The convenience of AI coding assistants is real, and I use them. But we are quietly training a generation of developers to accept generated code without reading it closely, and hardcoded secrets are the canary. If you cannot spot a literal credential in a diff, you are not reviewing code. You are approving it.
Security basics are not optional just because the code was generated quickly. Slow down for thirty seconds on every diff that touches configuration, network calls, or auth. That is where the damage lives.
Seaworthy builds tools that help teams catch exposed credentials and other common misconfigurations before they ship.
本文由 AI 生成,总结了公开可用的来源。
Seaworthy 会扫描你的代码仓库,发现类似本文提到的各种问题。
安全漏洞、泄露的密钥和配置错误——在部署前捕获。免费运行,无需账户。