Prompt Injection Explained: Securing AI Agents in Practice
Prompt injection is an attack where text the model reads, typed by a user or hidden in an email, web page or file, changes what the model does. It cannot be fully prevented today, so securing an AI agent means limiting what an injected instruction could achieve: fewer permissions, no unchecked path for data to leave, and human approval for risky actions.
Every useful AI agent reads text it did not write. It summarises emails, browses pages, opens attachments and reads tickets. The problem is that a language model does not reliably distinguish between instructions from you and instructions that happen to appear inside the content it is processing. To the model, it is all just text.
That makes prompt injection different from most security bugs. There is no single input to sanitise and no patch that closes it. This post explains how the attacks work, walks through a real one, and sets out the layered defences I use when building agents for clients.
What is prompt injection?
Prompt injection is when input to a language model alters its behaviour in ways the developer did not intend. OWASP lists it as LLM01 in its Top 10 for LLM applications, defining it as occurring "when user prompts alter the LLM's behavior or output in unintended ways", and noting that the inputs need not even be visible to humans, as long as the model parses them.
The root cause is architectural. A traditional application keeps code and data apart: SQL parameters, escaped HTML, typed APIs. An LLM application puts your instructions and the content it is processing into the same stream of tokens. A 2023 paper on indirect prompt injection by Greshake and colleagues, put it plainly: "LLM-Integrated Applications blur the line between data and instructions."
Direct vs indirect prompt injection
OWASP splits the attack into two kinds, and the difference matters for how you defend against each.
Direct prompt injection is when the person typing into your app tries to override its instructions. In OWASP's words, it occurs "when a user's prompt input directly alters the behavior of the model." The classic example is a user telling a support bot to ignore its rules and reveal its system prompt. The attacker is your user, so the damage is usually limited to what that user could already reach, provided your permissions are enforced in code rather than in the prompt.
Indirect prompt injection is when the malicious instruction arrives inside content the model processes on someone else's behalf. OWASP describes it as occurring "when an LLM accepts input from external sources, such as websites or files." The attacker never touches your app. They plant text in an email, a web page, a shared document or a code comment, and wait for an agent to read it.
Indirect injection is the dangerous one for agents, because the victim is the legitimate user and the agent acts with that user's permissions.
A real example: EchoLeak in Microsoft 365 Copilot
The clearest public example of indirect prompt injection against a major product is EchoLeak. According to The Hacker News, researchers at Aim Security found a critical flaw, CVE-2025-32711 with a CVSS score of 9.3, in Microsoft 365 Copilot. An attacker sends an email containing a hidden prompt to someone's Outlook inbox. Later, when an employee asks Copilot a routine business question, Copilot's retrieval pulls that email in alongside internal data, and the injected instructions cause it to leak data through Microsoft Teams and SharePoint URLs, with no click required from the user.
Aim described it as "an instance of a large language model (LLM) Scope Violation that paves the way for indirect prompt injection". Microsoft had already fixed it when the research was published, and the report says no customer action was required.
The details matter less than the shape. The agent had access to private data, it read untrusted content, and it had a way to send information out. Every one of those features was intended. The combination was the vulnerability.
Why can't you just filter out malicious prompts?
You cannot filter reliably because there is no fixed pattern to block. An injected instruction can be phrased politely, written in another language, split across a document, hidden in white text or encoded in a way the model understands and your filter does not. OWASP is candid about this: "it is unclear if there are fool-proof methods of prevention for prompt injection."
Simon Willison makes the same point about guardrail products in his post on the lethal trifecta. Vendors often claim to catch "95% of attacks", and he responds that "in web application security 95% is very much a failing grade." An attacker only needs the one phrasing that gets through.
Filters and classifiers still have a place as one layer. They raise the cost of casual attacks and give you signals to log. They just cannot be the thing you rely on.
The lethal trifecta: the combination to avoid
The most useful mental model I know is Willison's lethal trifecta. An agent is at serious risk when it combines all three of:
- "Access to your private data"
- "Exposure to untrusted content"
- "The ability to externally communicate in a way that could be used to steal your data"
His advice is direct: "The only way to stay safe there is to avoid that lethal trifecta combination entirely." EchoLeak had all three. Remove any one leg, and an injected instruction has far less to work with.
In practice, when I design an agent I list which legs it has and try to cut one. An agent that reads untrusted email and has private data should not be able to fetch arbitrary URLs or render images from unknown domains. An agent that browses the open web should not hold credentials to your CRM. If the product genuinely needs all three, the remaining defences have to be much stronger, and a human usually needs to approve the outbound step.
Defences that actually reduce risk
Effective defence is layered, with the assumption that injected text will sometimes reach the model. OWASP's mitigation list is a good backbone. It includes "constrain model behavior", "define and validate expected output formats", "enforce privilege control and least privilege access", "require human approval for high-risk actions", "segregate and identify external content", and "conduct adversarial testing and attack simulations". Here is how those translate into engineering work.
| Defence | What it stops | How I implement it |
|---|---|---|
| Least privilege | Injected text calling powerful tools | Small tool set per task, read-only by default, scoped credentials |
| Break the exfiltration path | Data leaving via links, images or requests | Allow-list outbound domains, strip or proxy links and images in output |
| Human approval | Irreversible actions taken on bad instructions | Confirm before send, delete, pay, share or change permissions |
| Permissions in code | Users talking the model past its rules | Authorisation checks in the tool layer, never only in the prompt |
| Structured outputs | Free-form instructions passing between steps | Schema-validated tool calls and results |
| Segregate untrusted content | Instructions blending with data | Mark external content clearly; keep it out of the system prompt |
| Logging and red-teaming | Attacks going unnoticed | Trace every tool call; test with planted instructions before launch |
A few of these deserve more detail.
Put permissions in the tool layer. A system prompt that says "never reveal other customers' data" is a request, not a control. The tool that fetches customer data should check who is asking and return only what they are allowed to see. If the model is tricked, the tool still says no.
Validate the shape of everything. When one step's output feeds the next step's input, schema-validated structured output narrows what an injection can smuggle through. I cover the mechanics in structured outputs for reliable JSON.
Treat approvals as product design. Approval prompts that fire on every action get clicked through. Reserve them for actions that are irreversible or that send data outside the organisation, and show the user exactly what will happen.
Test with your own attacks. Before launch, plant instructions in the kinds of content your agent reads, such as emails, pages and PDFs, and check what happens. Keep those cases in your test suite so later changes do not reopen them.
The same principles apply to MCP servers and tool integrations: expose the fewest tools that do the job, with the narrowest permissions. My walkthrough on building an AI agent with MCP and Claude shows where those boundaries sit, and protecting API keys in AI web apps covers keeping credentials out of the model's reach.
Work with me
I build AI agents and MCP servers with security designed in from the first tool definition: scoped permissions, approval steps and tested defences. See AI agent development and MCP server development, browse my work, and book a scoping call.
Frequently asked
What is the difference between prompt injection and jailbreaking?
Jailbreaking is an attempt to get a model to break its provider's safety rules, usually by the person typing. Prompt injection is broader: any input, including hidden text in content the model reads, that changes the behaviour of your application against your intent. For agent builders, indirect prompt injection is the bigger concern.
Can prompt injection be completely prevented?
No reliable method exists today. OWASP states that it is unclear whether fool-proof prevention is possible, so the practical goal is to limit the impact: least-privilege tools, no unchecked way for data to leave, and human approval for high-risk actions.
Does a good system prompt protect against prompt injection?
A well-written system prompt helps the model behave correctly in normal use, but it is not a security control. Injected instructions can override or confuse it, so any rule that matters, such as who can see which data, has to be enforced in code outside the model.
Are AI agents more vulnerable to prompt injection than chatbots?
Generally yes, because agents read more untrusted content and can take actions with real permissions. A chatbot that only answers questions has little to abuse; an agent that can read email, browse the web and send messages gives an injected instruction much more to work with.
Sources
Related services
Building something like this?
Naman Gundaniya takes on full-stack and AI projects fixed quote after a free 30-minute scoping call, replies within 24 hours.
Get in touch