mech.app
Security

Confused Deputy Attacks: Why AI Agents Expose Authorization Gaps You Never Wrote

AI agents reveal missing authorization checks in systems that relied on UI-layer enforcement, turning the confused deputy pattern into a practical exploit.

Source: stackoverflow.blog
Confused Deputy Attacks: Why AI Agents Expose Authorization Gaps You Never Wrote

In June 2026, attackers took control of more than twenty thousand Instagram accounts by asking Meta’s AI support assistant to attach a new email address to accounts they did not own, then requesting password resets. No exploit. No credential stuffing. Just a polite conversation with an agent that had the authority to execute the request and no mechanism to validate whether it should.

Meta later confirmed the assistant behaved exactly as designed. A separate authorization check was supposed to verify email ownership. That check never ran. The agent carried out a valid sequence of permitted operations for whoever was talking to it, and nothing downstream noticed.

This is not an AI problem. It is a systems problem that AI agents make visible.

The Confused Deputy Pattern

A confused deputy is a privileged process tricked into using its authority on behalf of a less-privileged party. The classic example: a compiler with write access to system directories, invoked by a user who passes a malicious output path. The compiler writes where the user cannot.

Agents are confused deputies by design. They hold credentials, API keys, database connections, and service accounts. They act on behalf of users who lack direct access to those resources. The agent’s job is to translate intent into privileged operations.

The security model assumes the agent will validate requests before executing them. But most systems never wrote those checks because they relied on something else: a human in the loop.

Where Authorization Actually Lived

Before agents, authorization often happened in three layers:

  1. UI enforcement: buttons disabled, forms hidden, routes blocked in the frontend
  2. Human discretion: support staff, approval workflows, manual review queues
  3. Backend checks: the code that actually validates permissions before executing

The first two layers were never meant to be security boundaries, but they became implicit ones. If a user could not reach the API endpoint through the UI, the backend check was never tested. If a support worker would refuse a suspicious request, the system behind them did not need to enforce it.

Agents bypass layers one and two. They call APIs directly. They do not see disabled buttons. They do not have discretion. If the backend check is missing or incomplete, the agent will execute the request.

What This Looks Like in Practice

An agent with database access receives a natural language query: “Show me all orders for customer ID 5281.”

The agent translates this into SQL:

SELECT * FROM orders WHERE customer_id = 5281;

If the agent has a service account with read access to the orders table, the query succeeds. The question is: did anything verify that the user talking to the agent is authorized to see customer 5281’s data?

In a traditional web app, this check might live in:

  • Middleware that injects user_id into the query context
  • A row-level security policy in the database
  • Application logic that filters results based on session state

If the agent bypasses the middleware (because it is not a browser session), skips the application logic (because it is calling the database directly), and the database has no row-level policy (because the service account was trusted), the authorization check does not happen.

Architecture: Where Checks Must Live

LayerTraditional AppAgent SystemRisk
FrontendButton visibility, route guardsNot presentAgent bypasses entirely
MiddlewareSession-based context injectionMay not apply to agent service accountAgent uses different auth path
Application LogicBusiness rules in handlersMay be skipped if agent calls lower layersAgent optimizes for directness
DatabaseRow-level security, viewsOften missing for trusted service accountsService account sees everything
Audit LogTracks user actionsMay log agent identity instead of end userAttribution lost

To secure an agent system, authorization must move to the lowest layer the agent can reach. If the agent can call the database, the database must enforce row-level security. If the agent can invoke an API, the API must validate the end user’s permissions, not just the agent’s credentials.

Least-Privilege Scoping for Agents

The naive approach: give the agent a service account with broad permissions and trust it to self-limit. This fails the moment the agent is tricked, buggy, or compromised.

A better model:

Capability tokens: Each agent request carries a token scoped to the end user’s permissions. The agent does not decide what it can do; the token does.

# Agent receives a scoped token with each request
def handle_user_query(query: str, user_token: CapabilityToken):
    # Token encodes: user_id, allowed_resources, expiration
    db_client = get_db_client(user_token)
    
    # Database enforces token scope at connection level
    result = db_client.execute(query)
    return result

Context propagation: The agent must pass the end user’s identity through every layer. This requires:

  • Structured logging that separates agent identity from user identity
  • API clients that accept a on_behalf_of parameter
  • Database connections that set session variables for row-level policies

Explicit permission checks: Before the agent executes any operation, it calls a policy engine that evaluates the user’s permissions against the requested resource.

def execute_action(action: str, resource: str, user_context: UserContext):
    if not policy_engine.authorize(user_context, action, resource):
        raise PermissionDenied(f"User {user_context.user_id} cannot {action} {resource}")
    
    # Proceed with privileged operation
    return perform_action(action, resource)

Observability and Failure Modes

Agents fail in ways that are hard to detect:

Silent authorization bypass: The agent executes a request it should not have. No error is logged because the operation succeeded. The only signal is an anomaly in access patterns.

Attribution loss: Logs show the agent’s service account performed an action, but not which user requested it. Incident response cannot trace the request back to the source.

Scope creep: The agent is granted a new permission to handle a legitimate use case. That permission is never revoked. Over time, the agent accumulates privileges it no longer needs.

Monitoring must track:

  • User-agent-resource triples: Who asked the agent to do what, and did the policy allow it?
  • Permission denials: How often is the agent attempting operations that fail authorization checks?
  • Privilege escalation attempts: Is the agent being asked to perform actions outside its normal pattern?

Deployment Shape

A secure agent deployment separates the agent’s execution environment from the resources it accesses:

  1. Agent runtime: Runs with minimal privileges. Cannot directly access databases, APIs, or secrets.
  2. Policy enforcement point: Sits between the agent and every backend service. Evaluates user permissions before forwarding requests.
  3. Credential vending service: Issues short-lived, scoped tokens to the agent based on the user’s identity.

This shape forces authorization checks to happen in a controlled layer that the agent cannot bypass.

Technical Verdict

Use this approach when:

  • You are deploying agents that act on behalf of multiple users
  • Your backend services were designed with implicit trust in the caller
  • You need to audit agent actions at the granularity of individual users
  • You are migrating from human-in-the-loop workflows to automated agents

Avoid this approach when:

  • Your agents only perform read-only operations on public data
  • You can afford to give each user a dedicated agent instance with isolated credentials
  • Your backend already enforces row-level security and attribute-based access control at every layer

The confused deputy problem is not new. Agents just make it impossible to ignore. If your authorization model assumed a human would catch mistakes, that assumption is now a vulnerability.