SSOGEN / Learn / The Confused Deputy Problem: From a 1988 Compiler to AI Agents

Learn

The Confused Deputy Problem: From a 1988 Compiler to AI Agents

The confused deputy problem is a program with more authority than its caller being tricked into using it for that caller; the fix is the caller's authority.

Updated

Flow of the confused deputy problem: a caller asks a program with more authority to act on its behalf
Neither side is malicious; the request just runs under the wrong authority.

The confused deputy problem is a flaw where a program (the deputy) holds more authority than the party asking it for something, and the asker gets the deputy to use that authority on its behalf. The deputy is neither malicious nor broken. It cannot tell whose authority a request should run under, so it uses its own.

The same mistake appears in:

  • a 1988 compiler;
  • cloud services that call each other;
  • OAuth proxies;
  • AI agents that call tools.

Hardy’s original case: a compiler and a billing file

Norm Hardy described the problem in “The Confused Deputy (or why capabilities might have been invented)”, ACM SIGOPS Operating Systems Review, volume 22, issue 4, October 1988. Wikipedia’s summary of the paper (read 2026-10-11) gives the case.

A compiler on a timesharing system let users name a file for debugging output. The compiler itself had permission to write statistics into a system directory. A user named the system’s billing file as the output file:

  1. The user had no right to write the billing file.
  2. The compiler did, and it used its own right.
  3. The billing data was overwritten.

The compiler checked what it was allowed to do, never what the caller was allowed to do.

Hardy’s fix, as the same summary puts it, is to bundle the designation of an object with the permission to use it.

The caller passes a capability, such as an open file handle, and the deputy can act only on what it was given.

Where the same flaw appears today

The details change but the pattern stays the same: a caller with little authority reaches a valuable resource through a deputy with broad authority.

Scenario The deputy Authority it wrongly uses Control that prevents it
Hardy’s compiler The compiler Its write permission on the billing file Pass the caller’s capability, not a name
AWS cross-account role A third-party service that assumes a role in your account The role ARN another customer also supplied A unique ExternalId in the role’s trust policy
AWS cross-service access A service principal such as CloudTrail The bucket policy’s trust in that service aws:SourceArn or aws:SourceAccount conditions
MCP proxy to a third-party API The MCP proxy server A consent cookie tied to its static client ID Per-client consent and exact redirect URI matching
Token passthrough An MCP server Tokens it accepts without checking they were issued to it Audience validation; refuse tokens meant for others
AI agent calling tools The agent A shared or broad credential that outranks the person A delegated credential with the person as subject, the agent as actor, narrow scope

The AWS and MCP rows come from the vendors’ own documentation, cited below. The last row is our reading of how the same pattern applies to agents.

Cloud services: external IDs and source conditions

AWS’s documentation on the confused deputy problem (read 2026-10-11) describes two cases.

Cross-account access

You hire a monitoring company and give it a role to assume. Another of its customers learns or guesses your role ARN, which AWS notes “isn’t a secret”, and asks the company to use it.

The company then assumes your role for the wrong customer. The fix is an external ID:

  1. The company generates a unique value per customer.
  2. You put it in the role’s trust policy as an sts:ExternalId condition.
  3. The company sends the matching value on every call.

The other customer cannot change which ID the company sends.

Cross-service access

A bucket policy that trusts cloudtrail.amazonaws.com with no conditions would accept logs from an unauthorized actor in another account who knows the bucket name.

AWS recommends the global condition keys wherever a service principal gets access. With them, the policy checks whom the service acts for.

  • aws:SourceArn. A global condition key AWS recommends for service principals.
  • aws:SourceAccount. A global condition key AWS recommends for service principals.
  • aws:SourceOrgID. A global condition key AWS recommends for service principals.
  • aws:SourceOrgPaths. A global condition key AWS recommends for service principals.

A worked example with fictional names: Acme’s bucket acme-audit-logs trusts a log service. Without a condition, any tenant of that service who knows the name can write to it.

Adding "aws:SourceAccount": "111122223333" limits writes to logs the service produces for Acme’s account.

OAuth proxies and MCP

The MCP security best practices page (specification 2026-07-28, read 2026-10-11) names the problem for MCP proxy servers. It is exploitable when all four conditions hold:

  • the proxy uses a static client ID with a third-party authorization server
  • clients can register dynamically
  • the third-party server sets a consent cookie after the first approval
  • the proxy skips its own per-client consent

The attack:

  1. An attacker registers a client with a malicious redirect URI and sends the user a link.
  2. The consent cookie makes the third-party server skip the consent screen.
  3. The authorization code goes to the attacker.

The specification is direct: proxy servers “MUST implement per-client consent”. The consent page must name the client and show the requested scopes and the registered redirect URI. Redirect URIs must match exactly, with no patterns.

Token passthrough

The second MCP form is token passthrough.

The authorization section says servers “MUST validate that access tokens were issued specifically for them as the intended audience” and “MUST NOT accept or transit any other tokens.”

Audience restriction comes from RFC 8707, resource indicators (read 2026-10-11), whose premise is that an access token must be valid only at a specific protected resource.

The full flow is in MCP authentication.

AI agents acting for people

An agent that calls tools is a deputy by design, and the two common shortcuts both repeat Hardy’s compiler:

  • On the person’s own token, the agent can do anything the person can.
  • On a shared service key, a person with little access can ask the agent for data they should not see, while the system sees only the agent.

RFC 8693 (read 2026-10-11) gives the words for the fix.

Under delegation, A “still has its own identity separate from B” and “any actions taken are being taken by A representing B”.

The act claim (section 4.1) is how a token says so.

A fictional token for Acme:

{
"iss": "https://login.acme.example",
"sub": "maria.lopez",
"act": { "sub": "agt_inv01" },
"aud": "https://invoices.acme.example/api",
"scope": "invoices.read"
}
  • sub is Maria.
  • act is the agent.
  • aud is the one service that may accept the token.
  • scope is what the token allows.

The invoices API can then check Maria’s rights and the agent’s, and log both. OAuth token exchange covers how to obtain such a token.

The token changes nothing unless the resource server checks aud, scope and act. Without those checks, delegation reads as impersonation.

What to check in your own systems

  • Does every service that acts for someone get that someone’s identity, not just its own?
  • Does each token name one audience, and does each server refuse the others?
  • Do you record consent per client, not per user or per cookie?
  • Does every trust grant to another service carry a condition naming whom it acts for?
  • Can one log line tell you both the person and the program?

How SSOGEN applies this to agents

The credential SSOGEN issues to an AI agent names the person in sub and the agent in act.

  • One application. It covers one application and its scopes.
  • Short life. It lasts 240 seconds by default, never more than 270.
  • Consent first. The person first approves the agent on a consent screen.
  • Scope on every call. The Gateway checks scope on every tool call.
  • No passthrough. The Gateway never forwards the agent’s token to your server, so token passthrough cannot occur.

The scopes you grant set the boundary: once an agent may read data, what it does with that data is governed by those grants. See AI agent identity and the MCP Gateway.

Questions people ask

What is an example of the confused deputy problem?
In Hardy’s 1988 example, a compiler could write a system billing file, and a user told it to send debug output there. The user had no right to that file; the compiler did, and it used its own right. The fix is to pass the caller’s authority, not a bare file name.
What is the confused deputy problem in AWS?
AWS describes it as an entity without permission coercing a more privileged one to act. Across accounts, an external ID in the role trust policy stops it. Across services, aws:SourceArn or aws:SourceAccount conditions stop it by making a service principal prove which resource or account it acts for.
How does the confused deputy problem apply to MCP?
The MCP specification names it for proxy servers that use a static client ID: a leftover consent cookie lets an attacker’s dynamically registered client skip consent. The specification requires per-client consent and exact redirect URI matching, and it requires rejecting any token not issued to the server itself.
How do you prevent the confused deputy problem for AI agents?
Give the agent a credential that names both the person and the agent, as RFC 8693 delegation does with the act claim. Limit it to one audience and a few scopes, keep its lifetime short, and have each tool server check audience, scope and actor on every call.