The confused deputy
Your agent has powers your user does not. An attacker who cannot open a door themselves simply asks the agent to open it for them.
In 60 seconds
The confused deputy
Your agent has powers your user does not. An attacker who cannot open a door themselves simply asks the agent to open it for them.
How it shows up in practice
- 1
Multi-tenant leakage
One agent serves every customer with one database connection. Customer A asks about "my recent orders" in a way that widens the query, and sees customer B's. The database was asked politely and answered honestly. - 2
Privilege ladder
A support agent can reset passwords because that is a support job. A user talks it into resetting an admin's password. The tool worked exactly as specified. - 3
Internal network reach
Your agent runs inside the network and can reach internal services. An injected instruction has it fetchhttp://internal-admin/and summarise the page. That is server-side request forgery, performed by your own agent. - 4
Shared cache poisoning
An agent caches results to be fast. One user's poisoned result gets served to the next user. The attack now spreads by itself.
The fix: carry the user's identity, not the agent's
- One service account with wide rights.
- The agent decides which user it is acting for.
- Permission checks happen in the prompt.
- Logs show "agent did X" with no user attached.
- Per-request token scoped to the actual end user.
- The runtime pins the user identity; the model cannot change it.
- Permission checks happen in the database and API layer.
- Logs show "agent did X on behalf of user 4471".
Watch and read more
Lab
A confused-deputy exploit against your own multi-tenant agent, then the fix.
The problem
# WRONG: one connection, model chooses the filter
def search_orders(query, customer_id=None):
sql = "SELECT * FROM orders"
if customer_id:
sql += f" WHERE customer_id = '{customer_id}'"
return db.execute(sql)
# RIGHT: identity pinned by the runtime, invisible to the model
def search_orders_scoped(query, *, caller_id):
return db.execute(
"SELECT * FROM orders WHERE customer_id = %s AND description ILIKE %s",
(caller_id, f"%{query}%"),
)You are done when
Hard questions
Try to answer before you reveal. If you can answer these, you understood the lesson.
Q1Why must customer_id be a keyword-only runtime argument rather than a tool parameter the model fills?Reveal
Q2Your agent runs inside the VPC and has a fetch tool. Name the attack and the two controls that matter.Reveal
http://169.254.169.254/ (cloud metadata, often containing role credentials) or an internal admin service, and summarise the result. Controls: block link-local and private ranges at the network layer, not with a URL check in the tool — and require IMDSv2 or remove instance credentials entirely. A regex on the URL is defeated by a redirect or a DNS name that resolves internally.Questions people ask
Isn't this just an access-control bug?
Yes — that is exactly what it is, and that is good news. It is an old, well-understood class of bug with well-understood fixes. Agents simply make it far easier to create by accident, because the natural way to build one is with a single powerful service account.
How do I pass user identity through a chain of agents?
Propagate a scoped token with every internal call and check it at every boundary, the same way a well-built microservice architecture does. Never let an internal hop upgrade privileges just because it is internal.
Is SSRF really a concern for agents?
Very much so. An agent with a fetch tool that runs inside your network is a request-forgery engine. Block internal address ranges and cloud metadata endpoints at the network layer, not with a URL check in the tool.
What about agents that legitimately need admin rights?
Then they should not also read untrusted content. Split the system: an admin-capable agent that only ever receives structured, validated input from your own code, and a separate untrusted-content reader with no rights at all.
Lesson test
5 questions. Get 3 right (60%) to pass and complete this lesson.
Sign in with your phone number to take the test and save your progress