Secret management can look like a solved problem because the basic answer is familiar. Do not put credentials in the repository. Use a vault. Rotate them. Scope them narrowly. Nobody argues with any of that.
Even so, a substantial proportion of incidents still begin with a credential somewhere it should not be. The advice has stayed the same. The consequence has changed, because the thing holding the credential may now be a process that decides what to do next.
A conventional web application responds to requests. An agent can choose its next action. That changes the damage a leaked credential can do.
A leaked agent credential gives away combined access
A leaked database credential in a conventional system gives an attacker read or write access. What they can do is bounded by the schema and by their own effort. It is bad, but the damage is finite, human-paced and usually noisy.
An agent’s credential set is different in kind. The agent was given access to several systems deliberately, because its usefulness depends on being able to act across them.
Those systems may include:
- An email integration.
- A database connection.
- An internal API token.
- The ability to make outbound requests.
Each of those permissions was granted separately, and each one may have seemed reasonable at the time. Together they form a composite privilege that nobody wrote down. An attacker who obtains them inherits the union.
That is the business problem. The leaked credential no longer exposes one narrow doorway. It can expose the combined authority of the agent across systems that were connected so the agent could be useful.
Agent tokens are easier to expose than older application secrets
There are structural reasons why agent credentials leak more readily. The prompt assembly path frequently includes configuration. In plain terms, the instructions and context sent to the model often get built from settings that may include sensitive values.
Development creates another path. Debug logging during development captures full request bodies, and those logs are shipped to a service that a great many people can read. Error tracking captures exceptions with context. The agent’s own output may also quote its configuration if asked cleverly. That leak path has no analogue in conventional systems.
The common exposure points are:
- Configuration included in the prompt assembly path.
- Debug logging that captures full request bodies.
- Logs shipped to a service that a great many people can read.
- Error tracking that captures exceptions with context.
- Agent output that quotes its configuration if asked cleverly.
We have found live credentials in error tracking twice in the last year. Both were in code we inherited. In both cases, a developer had captured a payload during an integration and never removed it.
Give the agent credentials for the task, at the moment of use
The single most valuable change is to stop giving an agent a standing set of credentials and start issuing narrow ones at the moment of use.
If a task is to summarise one document, the token it holds should permit reading that document and nothing else, and should expire in minutes. If a task is to update a record, the credential should permit updating that record and no others.
This is more work than a service account with broad permissions. It converts a catastrophic leak into an inconvenient one, because a stolen token is worth almost nothing outside the context it was issued for.
Most cloud providers and identity systems support this pattern well. What is usually missing is the discipline to use it. A single powerful credential makes development easier, and nobody revisits it before launch.
The agent must never be able to grant itself more access
There is one category worth calling out explicitly. An agent should never hold a credential capable of granting credentials.
That means:
- No access to the secrets manager itself.
- No ability to create service accounts.
- No permission to modify its own policy.
Any such capability converts a bounded compromise into an unbounded one. An attacker who can persuade the agent to mint a new long-lived token has escaped every other control you built. The mechanism will look like ordinary use of an authorised capability.
This is the same privilege escalation logic that has governed operating system design for decades. It applies here without modification.
Rotation only matters when it has been tested
Everybody has a rotation policy. Far fewer have rotation that has been executed. A rotation procedure that has never been performed is a document rather than a control.
We test it deliberately. Pick a credential, rotate it during business hours, and see what breaks. Something usually does. The thing that breaks is nearly always a copy of the secret somewhere nobody remembered.
Those forgotten copies tend to be in places like these:
- A scheduled job.
- A monitoring check.
- A colleague’s local environment.
Finding that during a planned exercise is enormously cheaper than finding it at two in the morning when the credential has been rotated because it leaked.
Normal behaviour is a weak signal when the agent is supposed to do the work
Traditional detection leans on behavioural anomaly. A user account reading ten thousand records at three in the morning is unusual, and the unusualness is the signal.
An agent legitimately reads ten thousand records at three in the morning. That is its job. The volume, the timing and the pattern are all normal. The signals that would catch a human intruder are useless.
What works instead is asserting on purpose rather than on pattern. Every action carries the task it was performed for and the identity that initiated that task.
An action with no originating task, or with an identity that does not have the underlying permission, is anomalous regardless of how ordinary it looks. That requires the action to be issued through a deterministic layer, meaning a predictable control layer that knows where the action came from and why it was allowed.
That same architecture constrains prompt injection, where a prompt tries to make the system ignore its intended limits, and it is one of several reasons to build it.
Outbound network access can leak data too
Teams control what an agent can read carefully and frequently leave its network egress wide open. Egress is outbound network access, the ability to send requests out from your systems.
An agent that can make arbitrary outbound requests can send anything it can see to anywhere. The request will originate from inside your network with your addresses attached.
The mitigation is an allowlist of destinations, enforced at the network layer rather than in application code. The reason is that the whole premise is that application behaviour may be influenced.
That is a small piece of infrastructure work that removes an entire category of exfiltration, meaning data being sent out where it should not go.
Let the agent act for a user without becoming a superuser
The hardest design question in this area is delegation. A user asks an agent to do something that requires their authority.
The lazy implementation gives the agent a powerful credential and has it filter results by the requesting user. That puts the authorisation decision in application code that a persuasive document can influence.
The correct implementation carries the user’s own identity through to the underlying system. Then the database refuses the query, rather than the application declining to make it.
Token exchange patterns exist for exactly this and are worth the setup cost. The agent presents evidence of who asked. It receives a short-lived credential that carries that user’s permissions and no more. Every downstream system enforces its own access control as it always did.
The agent becomes a courier rather than a superuser. That means a compromised agent can only ever do what the person on whose behalf it is acting could already have done.
The practical benefit beyond security is that the audit trail becomes honest. Instead of every action in the logs being attributed to one service account, each one carries the human who initiated it. When somebody asks who deleted a record, the answer is a name rather than an assistant.
Non-production environments often become the soft target
A pattern we see repeatedly is that the production posture is careful and everything upstream of it is not. Staging holds a copy of production data because that made testing realistic. A demonstration environment was set up for a sales conversation and given real credentials because it needed to look convincing. A developer’s machine holds a long-lived token from an integration they were debugging in March.
Those examples all create the same credential exposure as production, with a fraction of the controls and typically none of the monitoring. Attackers know this. A staging environment with production data is a well understood soft target.
Our rule is that non-production environments never hold production credentials and never hold production data. That sounds absolute and occasionally is inconvenient.
The inconvenience is a synthetic data generator and a set of test fixtures, which is a fortnight of work once. The alternative is that your security perimeter is defined by the least careful environment anybody has ever created. That environment is usually one somebody spun up for a demonstration and forgot.
In the first week, we look for where secrets really are
What we do in the first week of an engagement is practical. We look for the places where credentials exist, where they have been copied, and whether rotation has actually happened.
- Scan the repository history, not just the current tree, because a removed secret is still in the history and is still valid.
- Enumerate every credential the system holds, what it can do, and when it was last rotated. The list is always longer than the client expects.
- Check error tracking and log aggregation for captured payloads containing secrets.
- Establish which credentials are shared between environments, which is the most common finding and the one clients are most surprised by.
- Confirm that a rotation has actually been performed for each one, with a date.
A service account that worked for a prototype became production risk
A client had built an internal assistant with access to their customer database, their document store and their email system. It was a sensible product and competently built. It held one service account with permissions covering all three because that had been the fastest way to get it working during a prototype.
The prototype went to production, as prototypes do. The service account’s credentials were in an environment variable, which is fine. They were also in a deployment script in the repository, which is not fine, and had been for seven months.
Nothing had happened. The repository was private and access was reasonably controlled. But the exposure was that any of the forty people with repository access held, in effect, the ability to read every customer record and send email as the company. None of them knew it.
The remediation took nine days. It included per-task credentials issued by an identity broker, egress allowlisting, secrets removed from history, and the service account deleted.
The client’s own summary afterwards was that they had spent a year thinking about model safety and had not once thought about what the model was holding.
Design around the damage if the agent is fully controlled
When an agent is given access to a system, ask what an attacker would obtain if they controlled it completely, and whether that outcome is acceptable.
If the answer is that they would obtain the ability to read one document for six minutes, you have designed well. If the answer is that they would obtain everything the agent has ever been able to touch, then the security of your system rests on nobody ever getting that far. That position does not survive contact with a determined adversary or an ordinary mistake.
More on how we approach this in cybersecurity services, and on scoped permissions as a product principle in BotUp.








