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:
- UI enforcement: buttons disabled, forms hidden, routes blocked in the frontend
- Human discretion: support staff, approval workflows, manual review queues
- 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_idinto 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
| Layer | Traditional App | Agent System | Risk |
|---|---|---|---|
| Frontend | Button visibility, route guards | Not present | Agent bypasses entirely |
| Middleware | Session-based context injection | May not apply to agent service account | Agent uses different auth path |
| Application Logic | Business rules in handlers | May be skipped if agent calls lower layers | Agent optimizes for directness |
| Database | Row-level security, views | Often missing for trusted service accounts | Service account sees everything |
| Audit Log | Tracks user actions | May log agent identity instead of end user | Attribution 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_ofparameter - 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:
- Agent runtime: Runs with minimal privileges. Cannot directly access databases, APIs, or secrets.
- Policy enforcement point: Sits between the agent and every backend service. Evaluates user permissions before forwarding requests.
- 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.
Source Links
- AI agents expose the security checks you never actually wrote (Stack Overflow Blog)
- Hacker News discussion