Full version with extended FAQ and framework methodology here.
This month I got an unusually large bill from Google.
My project's Google API key is public by design. The kind that lives in frontend code, which is architecturally normal and expected. Somewhere along the way, someone on my team also enabled Gemini API access on the same project. Gemini API is paid, expensive, and heavily targeted. The internet runs continuous scanners probing public resources for exactly that combination. They found it. By the time Google's budget alert arrived, the damage was already done.
Google compensated roughly 75% after support negotiations. The rest came out of my pocket. Search LinkedIn right now and you will find dozens of people with the identical story.
But the Google bill is just the entry point.
The AI Agent Security Problem Nobody Is Naming
Every time you ask an AI agent to do something real: configure Cloudflare, SSH into a server, push to a repo, hit an external API, you hand it a credential. That credential goes somewhere. Chat history. An environment file. Project context. From every one of those places it can walk out the door.
I used to paste API keys directly into chat windows. I know exactly how that sounds. The honest version: it felt completely natural because the workflow made it natural. Agent needs a key. I have the key. Done. It never registered as a security decision because the interface never asked me to treat it like one.
This one I am genuinely bad at. (I know I should revoke keys after one-off tasks. I tell myself I might use the same key again. Three months later it is still active and I have completely forgotten it exists.)
And sometimes the threat does not come through anything you did. Sometimes it comes through a package you have been trusting for years.
The Package Everyone Trusted for Two Hours and Fifty-Four Minutes
On March 31, 2026, an attacker compromised the npm account of axios's lead maintainer and published two backdoored versions of a library with over 100 million weekly downloads. The malicious versions added a single new dependency, plain-crypto-js, a name chosen to resemble the legitimate crypto-js. Axios's own code never called it. It did not need to. The dependency's job was to run automatically on install.
The maintainer confirmed in his own public post-mortem: the versions were live from 00:21 UTC to 03:15 UTC. Two hours and fifty-four minutes.
Inside that window, the dropper downloaded platform-specific payloads for macOS, Windows, and Linux, deleted itself, and restored a clean package.json to erase the evidence. The resulting RAT beaconed to its command server every 60 seconds: remote shell, file browsing, process listing, full system reconnaissance. Huntress confirmed active exploitation across all three operating systems during the window.
The detail that made me stop cold: Huntress found that even on the branch configured with modern OIDC trusted publishing, the CI/CD workflow still passed a long-lived NPM_TOKEN as a fallback environment variable. When both are present, npm defaults to the token. The more secure method was live. It just was not the one actually being used, because a legacy credential nobody had removed was sitting there quietly taking priority.
That is not axios being careless. That is the shape of how credentials accumulate invisibly long after the task they were granted for is finished. I recognized it immediately because it is exactly the shape of my own mistake.
That npm install axios could have been a developer doing routine maintenance. Or an agent running a dependency update at 2am with nobody watching. The package had been trusted for years. Neither you nor your agent had any reason to question it.
The Four-Layer Credential Containment Model
No single control makes this problem disappear. What these four layers do is shrink the blast radius at each point where a key could leak, so that no single failure is enough on its own to produce a four-figure surprise.
| Layer | What It Prevents | Effort | Residual Risk If Skipped |
|---|---|---|---|
| IP / network restriction | A leaked key being usable from anywhere on earth | ~15 min per key on any serious provider | A key restricted to your production server's IP is not worthless if it leaks. A key that works from any IP is a loaded weapon someone else can pick up. |
| Spending caps set low | A billing alert arriving after damage is already done | Minutes to configure | Set thresholds at ~20% of expected spend. An alert at your actual budget ceiling is not a warning. It is a receipt. |
| Key expiry and rotation | Standing access accumulating invisibly after tasks are finished | Discipline more than tooling | Every time I grant a key for a one-off task I tell myself I will revoke it. Three months later it is still live. I know this about myself and I still do it. |
| Agent never holds the key | Every leak vector above simultaneously: chat history, env files, logs, project context | Real engineering work: a broker injecting credentials directly into outgoing calls | The only fix that addresses the agent-specific risk at the architectural level. Hardware-backed storage (Apple Secure Enclave etc.) takes this further. Attack surface does not go to zero. It gets close. |
The first three layers you can implement this afternoon. The fourth is the one that actually changes the shape of the problem rather than narrowing it.
What Breaks Without These Layers
Assuming a public-by-design key is automatically safe. A frontend-exposed key is fine for its original scope. It stops being fine the moment anyone, including a well-meaning teammate, enables a paid capability on the same project without re-scoping the key.
Treating a budget alert as a safeguard. By the time it fires, the spend has happened. An alert is a notification, not a circuit breaker, unless paired with an actual spending cap that halts usage.
Trusting a package because it is popular. Axios had over 100 million weekly downloads and had been trusted by nearly every JavaScript project in existence. Popularity is exactly what makes a compromised maintainer account so valuable to an attacker.
Forgetting that OIDC is only as secure as what still runs alongside it. The axios incident's root cause was not a failure of trusted publishing. It was a legacy token never removed, still active as a silent fallback. Auditing what your system defaults to when two credentials are present is not optional once you have adopted a more secure method. The old one has to actually be gone.
Pasting keys into chat windows because the workflow makes it feel natural. It felt natural to me too. That is exactly the problem.
Neither incident needed a sophisticated attacker. Mine needed one forgotten API scope. Axios needed one forgotten token nobody had gotten around to removing. The damage in both cases was decided long before the problem became visible, by whatever had been left reachable in the meantime.
Written by Iaroslav Belkin, founder of Belkin Marketing, Web3 and AI content marketing, Hong Kong.
Sources: Wiz technical analysis, Huntress incident report, axios maintainer post-mortem.
Full article with FAQ here.
Top comments (0)