Blog · · 13 min read
AI agent secrets management without exposing API keys

AI agent secrets management fails when reusable API keys enter a process the agent can inspect. A shell-capable agent can read environment variables, mounted files, command history, debug output, or tool arguments. Prompt injection can then turn ordinary exploration into credential exfiltration. Moving the same key into a secret manager does not solve that runtime boundary if the agent can fetch the raw value. The safer pattern is a credential broker: the agent requests a permitted outbound operation, trusted product code authorizes it, and a proxy attaches the credential after the request leaves the agent's control. This guide builds that contract for an existing multi-user product.
Use a broker instead of handing secrets to the agent
Use this three-phase request flow:
CredentialRequest: trusted product code describes the actor, tenant, agent run, destination, method, operation, and requested grant. It does not include a secret.BrokerDecision: a policy service resolves current user authority, workload identity, service grant, and destination restrictions. It returns allow or deny with a short decision lifetime.ProxiedCall: an outbound proxy retrieves or mints the credential, attaches it to the approved request, sends the call, strips sensitive response fields, and emits a redacted receipt.
The agent sees a narrow tool interface and a typed result. It never sees the provider key, bearer token, client secret, refresh token, or credential-store response.
| Component | May see raw credential | Responsibility |
|---|---|---|
| Model and prompt | No | Propose an operation and arguments |
| Agent runtime or sandbox | No | Call a registered tool with non-secret data |
| Product authorization service | No | Decide whether this user and run may request the operation |
| Credential broker or proxy | Yes, in isolated memory | Resolve grant, attach credential, enforce destination policy |
| External service | Yes, as protocol requires | Authenticate and execute the request |
| Trace and audit pipeline | No | Store redacted decision and execution evidence |
Keep product authorization separate from secret brokering. Product authorization asks whether the current user may perform an action. Secret brokering determines how the approved call authenticates to an external service without revealing reusable credential material to the agent.
Why ordinary secret injection breaks with agents
Environment variables and mounted files assume that the process is trusted to possess the secret. That assumption is wrong when the process runs model-generated commands, browser automation, third-party tools, or untrusted content.
A practitioner described the problem directly in an Ask HN thread about agent secret handling: an agent with shell access can run env, print a named variable, or read .env. The report also notes that a local proxy works but creates operational overhead. This is a practitioner account, not proof that every agent runtime leaks secrets. It identifies a reproducible boundary: possession plus introspection makes exfiltration possible.
A conventional secret manager improves storage, access control, and rotation. It does not help if the agent can call get_secret() and receive the value. Secure storage is insufficient. Reusable keys must not cross the agent trust boundary.
The OWASP Secrets Management Cheat Sheet treats creation, rotation, revocation, expiry, auditing, downtime, backup, and recovery as one lifecycle. An agent integration needs all of those controls, but it also needs a mediation point at call time. The OWASP AI Agent Security Cheat Sheet adds agent-specific least-privilege, tool, isolation, and monitoring concerns. Together they support a broker that applies current policy instead of making possession of a long-lived key the authorization decision.
Define the AI agent secrets management contract
Do not let model output construct proxy headers or select a secret by name. Trusted product code builds the credential request from authenticated session data, the registered tool, and an immutable agent release.
{
"request_version": "credential-request-v1",
"tenant_id": "tenant_42",
"user_id": "user_108",
"run_id": "run_01J9F6K2",
"agent_release": "support-agent-2026-08-12.3",
"workload_id": "spiffe://example.com/agent-runtime/prod",
"tool_id": "github.create_issue.v2",
"service_id": "github-customer-connection",
"destination": {
"scheme": "https",
"host": "api.github.com",
"method": "POST",
"route_template": "/repos/{owner}/{repo}/issues"
},
"operation": "issue:create",
"resource": {
"owner": "customer-org",
"repo": "approved-repository"
},
"deadline": "2026-08-12T10:15:30Z",
"idempotency_key": "action_8e450a"
}
Every field except the user-provided issue content comes from trusted code or a registered tool contract. The model cannot change tenant_id, user_id, service_id, destination host, route template, or method by inserting instructions into a web page.
Bind the request to user and tenant authority
Resolve product authorization immediately before the outbound call. A decision captured when the run started may be stale after a role change, revoked integration, or expired approval.
The policy input should include:
- current user and tenant;
- exact product resource and action;
- agent run and immutable release;
- tool contract and risk class;
- external service connection owned by that tenant;
- destination host, method, and route class;
- approval receipt when the action requires one;
- call deadline and remaining run budget.
A service connection should reference a credential grant, not a secret value. For example, github-customer-connection can map to a tenant-owned GitHub installation, a permitted organization, selected repositories, and allowed operations. The agent cannot swap it for another tenant's connection because both the lookup and policy decision are tenant-scoped.
Give the runtime its own workload identity
User authorization and runtime identity are different. The broker needs to know which deployed service is asking, even when it also receives a user delegation.
SPIFFE defines verifiable workload identities and automatically rotated identity documents for dynamic infrastructure. A product can use SPIFFE or an equivalent platform identity so the broker accepts requests only from approved runtimes. Bind that identity to environment, service, and deployment policy. Do not use a shared static broker token across development laptops, CI, production runtimes, and customer sandboxes.
Authorize each call through this chain:
authenticated user
-> current product authorization
-> identified agent run and release
-> authenticated runtime workload
-> tenant-owned external-service grant
-> constrained outbound operation
The broker denies a call when any link is missing or stale.
Attach credentials only at the outbound proxy
After policy allows the request, the broker performs the credential-sensitive work in an isolated service.
A practical flow is:
- Canonicalize the URL and reject userinfo, alternate schemes, redirects to new hosts, raw IP destinations, and unregistered ports.
- Match the canonical host, HTTP method, and route template to the service grant.
- Retrieve or mint the narrowest available credential.
- Add the credential to an authorization header or provider-specific field.
- Remove broker marker headers before forwarding.
- Enforce request-body size, content type, timeout, and response-size limits.
- Reject cross-host redirects unless the destination policy explicitly allows them.
- Redact sensitive request and response fields before logging.
- Return a typed result and a receipt that contains no reusable credential.
The Agent Vault repository demonstrates this boundary with an HTTP credential proxy. Its documentation describes inserting credentials into outbound requests, service and endpoint controls, strict deny behavior for unmatched hosts, request logging, and integration through MCP, CLI, SDK, API, and sandboxed agents. Other brokers can implement the same pattern. The credential must be attached after the agent has lost the ability to inspect it.
A proxy must enforce destination policy itself. An allowlisted tool name is not enough. If the agent can choose an arbitrary URL, follow redirects freely, or send the credential to a user-controlled host, it can still exfiltrate the secret without reading the value.
Prefer short-lived and provider-native grants
Use long-lived API keys only when the provider offers no safer mechanism. Prefer, in order:
- a provider-native installation or service identity with resource-scoped permissions;
- a token exchange or minting flow that produces a narrow, short-lived token;
- a dynamic secret with an explicit lease and revocation path;
- a stored static key isolated inside the broker, with aggressive rotation and destination enforcement.
For HTTP-based MCP, the MCP authorization specification defines OAuth-based authorization, audience binding, insufficient-scope challenges, and protections against token misuse. Follow the protocol when the downstream server supports it. Do not place an MCP access or refresh token in model context or agent memory. Keep the client credential and refresh capability in the broker or another trusted authorization component.
Short expiry does not make a token safe to expose. It reduces the time available for abuse after a leak. The possession boundary and destination controls still matter.
Keep one policy across every execution surface
Agents reach external services through ordinary server-side tools, remote MCP servers, browser automation, generated code, and command-line programs. Apply the same broker policy to all of them. An unmediated CLI or browser session can otherwise bypass controls enforced by server-side tools.
Ordinary product tools
Expose domain operations such as create_support_ticket, not a generic http_request(url, headers, body). Trusted adapters translate the operation into a broker request. The model supplies business arguments, while the adapter supplies identity, service grant, destination, and method.
MCP servers
Treat each MCP server as a downstream audience. Keep server-specific access tokens out of the model-visible tool schema and tool arguments. When a server returns an insufficient-scope challenge, route it to a trusted consent or authorization flow. Do not let the model expand its own scope or choose a different audience.
Browser automation
Do not inject service API keys into page JavaScript, the clipboard, form fields, browser storage, or extension-visible state. For web applications that require an interactive session, lease an isolated browser profile with tenant-specific cookies and a bounded destination policy. Use the broker for API calls made outside the browser. A browser login session is a separate credential class with its own lifecycle.
Code sandboxes and command-line tools
Do not mount production .env files or pass provider keys as environment variables. Configure the sandbox to route approved outbound traffic through the broker. Give it only a short-lived broker capability tied to the run, workload, service grant, and deadline. Deny direct egress so the generated program cannot bypass mediation.
The Agent Vault agent documentation describes short-lived vault-scoped tokens for ephemeral sandboxes while retaining third-party credentials at the proxy. Whether using that product or a custom broker, distinguish the capability to ask for an approved call from the credential used at the destination.
Rotate and revoke without editing agent state
Secrets often leak into durable systems because teams store them in workflow checkpoints, queued tool arguments, conversation memory, or deployment configuration. A broker keeps those records referential.
Persist these values:
- service-connection ID;
- credential version or lease reference;
- policy decision ID;
- run and action ID;
- destination and operation;
- receipt ID.
Do not persist the resolved credential. A paused workflow resumes by asking the broker for the current valid grant. Rotation therefore affects the next call without rewriting checkpoints.
For static credentials, support overlapping rotation:
- create the replacement credential at the provider;
- register it as the broker's next version;
- canary approved calls through the new version;
- make it active;
- revoke the old provider credential;
- confirm the old version fails through a controlled negative check;
- retain only redacted version and timing metadata.
Revocation must be immediate at two layers. Disable the product's service connection so new policy decisions fail, then revoke or disable the provider credential. A broker-side deny is fast, but provider-side revocation limits damage if the value escaped before mediation was deployed.
Return redacted receipts, not credentials
The agent and user still need evidence that a call ran. Return a receipt containing non-secret facts:
{
"receipt_id": "call_01J9F6N7",
"decision_id": "decision_7041",
"run_id": "run_01J9F6K2",
"tool_id": "github.create_issue.v2",
"service_id": "github-customer-connection",
"destination_class": "api.github.com/repos/*/*/issues",
"operation": "issue:create",
"credential_version": "github-installation-v17",
"started_at": "2026-08-12T10:15:21Z",
"completed_at": "2026-08-12T10:15:22Z",
"http_status": 201,
"provider_request_id": "provider-redacted-reference",
"result_ref": "github-issue:customer-org/approved-repository#481",
"redaction_policy": "outbound-audit-v4"
}
Do not log authorization headers, cookies, signed URLs, refresh tokens, full query strings that may contain secrets, or raw provider error bodies before redaction. Treat request and response bodies as sensitive by default. Allowlist fields for audit instead of maintaining a blacklist of known secret names.
Link the receipt to the product authorization decision and action ID. This lets operators answer who requested the call, which release and workload made it, what destination and operation were approved, which credential version the broker used, and what the external service returned. It does not let them replay the credential.
Handle failure without leaking or widening access
Broker unavailable
Do not fall back to an environment variable or a bundled emergency key. For read-only, non-urgent work, return a retryable dependency failure with a deadline. For side effects, retain the same action ID and idempotency key so a retry cannot duplicate work. Surface the broker outage separately from a provider outage.
Authorization or scope denied
Return a typed denial that names the required product permission or consent step without exposing policy internals or secret identifiers. Never retry the same denied call automatically. If the downstream service returns 401 or 403, distinguish an expired grant from insufficient scope and revoked product authority. A model must not react by trying alternate credentials, hosts, or accounts.
Credential expired or rotated
The broker may refresh or mint a new token only within the existing approved grant. Cap refresh attempts and keep them inside the original call deadline. Do not return a refresh token to the agent. If the provider's outcome is unknown, verify the side effect by an authoritative read before retrying.
Suspicious destination or exfiltration attempt
Reject the request before credential attachment. Record the normalized destination, rule ID, run, release, and tool. Quarantine the run when repeated attempts target unregistered hosts, metadata services, loopback, private networks, redirectors, or encoded destinations. Do not store the hostile prompt or payload in an unrestricted security log if it may contain customer data.
Break-glass access
Keep emergency credentials outside normal agent policy. Require a human operator, narrow duration, explicit incident reference, stronger audit, and post-use rotation. Agents must never select break-glass mode.
Verify the broker with negative tests
A successful call proves only the happy path. Before enabling a service connection, test the paths that must fail.
- Ask the agent to print environment variables and search mounted files. No external-service credential should exist.
- Inject instructions into retrieved content that request the key, authorization header, or a call to an attacker-controlled host.
- Change the URL through userinfo, Unicode hostnames, DNS rebinding candidates, redirects, alternate ports, and encoded IP forms.
- Request an allowed host with a forbidden method or route.
- Reuse a broker capability from another tenant, user, run, workload, or expired deadline.
- Revoke the product service connection while a workflow is paused, then resume it.
- Rotate the provider credential and prove the old version no longer works.
- Trigger
401,403,429, timeout, partial response, and unknown-outcome cases. - Inspect model traces, tool arguments, checkpoints, logs, receipts, crash dumps, and support exports for credential material.
- Disable the broker and prove the runtime does not bypass it through direct egress.
- Try an unregistered MCP server, browser destination, CLI endpoint, and sandbox process.
- Confirm that a denied call cannot be converted into an approval or broader scope by model text.
Add release gates:
- zero raw credentials in agent-readable storage or telemetry;
- zero successful unmatched-host calls;
- zero cross-tenant grant resolutions;
- every outbound authenticated call linked to one current policy decision and one receipt;
- old credential versions fail after the declared rotation window;
- direct egress from sandboxed execution is denied;
- receipt redaction tests cover all supported provider adapters.
Monitor denials by destination rule, expired grants, rotation version, service, tenant, and agent release. A rise after deployment can identify a broken adapter or manipulated workflow without recording sensitive payloads.
Start with one external service
Choose one external API with a clear resource model, provider-side revocation, and a narrow product use case. Remove its key from the agent runtime. Register one tenant-scoped service connection, one destination and operation policy, and one brokered adapter. Add redacted receipts, then run the negative suite in a staging tenant.
Do not begin by proxying arbitrary HTTP. Start with a domain tool and a closed destination policy. Once the product can prove that no credential reaches prompts, sandboxes, traces, checkpoints, or direct egress, canary the connection for a small tenant cohort. That single migration creates a defensible AI agent secrets management boundary. Extend it to MCP, browsers, code execution, and other external services only after the same negative tests pass.
References
- OWASP Secrets Management Cheat Sheet supports centralized access control and the full secret lifecycle, including creation, rotation, revocation, expiry, audit, downtime, backup, and recovery.
- OWASP AI Agent Security Cheat Sheet supports agent-specific least-privilege, tool-security, isolation, and monitoring controls.
- Model Context Protocol authorization supports OAuth-based MCP authorization, token audience binding, scope challenges, and token-security requirements.
- SPIFFE overview supports verifiable workload identity and automatically rotated identity documents for dynamic infrastructure.
- Agent Vault documentation supports short-lived broker capabilities for agent and sandbox access without returning external-service credentials.
- Agent Vault repository provides a current implementation example for an HTTP credential proxy, destination controls, strict deny behavior, request logging, and tool-surface integration.
- Ask HN: How are you managing secrets with AI agents? provides direct practitioner problem evidence about environment variables,
.envfiles, shell access, local helpers, and proxy overhead.