LLM API keys are billing credentials
A leaked language model API key does not give access to your database. It gives access to your bill. The distinction sounds rhetorical until the day the statement arrives.
What the campaign bills
Google published an alert about LLMjacking campaigns: attackers retrieve model API keys and run inference on the victim’s account. BleepingComputer sums up the order of magnitude in its headline, $1 million in AI cloud charges. I know neither the details of the campaign nor the victims, and I am not going to invent them. The mechanism is enough: the stolen key is not used to read something on your side, it is used to consume.
That is the difference with a classic application secret. A stolen database password translates into an exfiltration, an access, a modification. A stolen model key translates into a line on a statement. Nothing is broken, nothing is unavailable, and the service keeps answering normally while the meter runs.
A billing credential before being a secret
Treating this key as a configuration secret, that is, as a value you put in a file, inject at deployment and forget, misses the nature of the problem. A configuration secret is protected by file access control. A billing credential is protected by what it allows to spend.
The consequence is practical. The controls that already exist (rotation, restricted scope, budget alerts, per-service quotas, usage logging) are only worth something if they are designed for an object that commits money. A budget alert that fires when the monthly cap is almost reached does not help if the consumption happens in one night. A quarterly rotation is useless if the key stays valid after leaving the repository.
What I do in my projects
Three measures, which I present as a baseline and not as a security architecture.
One key per service. Not a key shared between the backend, the worker and the admin script. If the worker leaks, I revoke the worker, and I know what it consumed.
Never hardcoded. Keys live in the environment or in a secrets manager, never in the repository, never in a versioned configuration file. It is the easiest rule to keep, and the one you break fastest while debugging on a Friday night.
Capped budgets. On my platform, accounts are multi-tenant with quotas, and the cap is a property of the account, not a recommendation. A hard cap turns a leak into a short incident instead of a long one.
What to do before being found
Revoke before investigating. If a key may have been exposed, revocation comes before analysis. Looking for the origin of a leak while the key stays active is funding the investigation.
Separate keys by environment and by service, with a cap per key. A staging key that can spend the production budget is a design error, not an accident.
Log usage on the provider side and on the application side, then compare. The provider says what was consumed, your application says what it requested. The gap is the signal.
Put the alert on consumption, not on the budget. A spike within an hour is visible. A budget percentage is not.
Where this does not apply
This approach has a limit, and I prefer to name it. The hard cap protects the bill and breaks the service: when the quota is reached, the feature stops for everyone, including legitimate users. On a product where the model call is the core of the experience, a low cap is a scheduled outage. You have to choose between the capped bill and availability, and that choice is made with the product, not in the configuration file.
Second limit: I have no figure on what these measures prevent. I have not measured a drop in incidents, because there was no incident of this kind to measure. A rule that has never been used is not proven, it is only in place.
Source: Google LLMjacking attacks rack up $1 million in cloud AI charges, BleepingComputer.