AI Security & Governance

Indirect Prompt Injection as the Default Condition

Designing on the assumption that the injection lands.

Chapter 3 of 1211 min readOpen access

Indirect Prompt Injection as the Default Condition is the assumption that replaces the search for a fix. Instructions arrive inside content the agent was asked to read, they need not be visible to a human, and the practical question stops being how to prevent them and becomes what the agent can do while it is following them.

Key takeaways

  • Direct injection is a user typing something into a box. Indirect injection arrives inside data the agent reads, which is why every control you put on the user's message leaves it untouched.
  • OWASP's 2025 prompt injection entry states that the vulnerability arises from the nature of generative systems and that it is unclear whether fool-proof prevention exists. That sentence is a design input, not a counsel of despair.
  • Detection is worth having and is not a control. Any mitigation that fails while the model is following an attacker's instructions is a layer, and layers belong in a different column.
  • Retrieved documents, tool output, web content and other agents' messages are all delivery paths. So is the agent's own earlier output, once it has passed through a store a user can influence.
  • The design move is to bound consequences rather than filter inputs, which is the tool surface, the credentials, the egress and the approval limits that every later chapter builds.

Read this after Chapter 2, whose map showed untrusted text reaching the context with nothing marked on the arrow, and before Chapter 4, which is the first control that actually holds. The article-length version of this argument is prompt injection defence in depth.

An engineer pastes a link into an internal tool and asks the agent to summarise the page. The summary comes back accurate, well organised and unremarkable.

Underneath it, the agent made a second tool call that nobody asked for. The page carried a short paragraph at the bottom, in pale text, addressed to any assistant processing the document rather than to a reader.

Nobody typed a malicious instruction. Nobody needed to.

Direct and indirect are different problems

The two get filed under one heading, and the filing hides the part that matters.

Direct injection is a user typing an instruction that tries to override the system prompt. It is a real problem with a familiar shape, since the attacker is the user, and the blast radius is bounded by what that user was allowed to do anyway.

Indirect injection is different in the one way that changes the architecture. The instruction arrives inside content the agent was told to process, so the attacker never authenticates, never appears in your logs as an actor, and never needs an account. They need only to place text somewhere your agent will eventually read.

The consequence is that input validation on the user's message, which is where teams instinctively start, does not touch the problem. The malicious text is not in the user's message. It is in the document the user legitimately asked about.

The sentence that ends the prevention debate

There is a specific published statement worth quoting in design reviews, because it saves a quarter of arguing.

OWASP's 2025 prompt injection entry records that the vulnerability arises from the nature of generative AI systems, and that it is not clear whether fool-proof prevention methods exist. It also records that injected content need not be human-visible, so long as the model can parse it.

That is a standards body declining to promise a fix. Take it at face value.

What follows is not despair, it is a budget decision. Effort spent trying to reach zero injection has an unknown and probably unreachable ceiling, while effort spent bounding what a compromised agent can reach has a known ceiling and compounds with every other control you own.

So the design assumption for the rest of this book is stated once and not revisited. The injection lands. Now what.

The delivery paths are features you already shipped

Nobody adds an injection surface on purpose. They add a capability, and the surface arrives with it.

Retrieval is the obvious one, since a vector store returns text written by whoever authored the corpus, and a document from a public source is a document from a stranger. Web fetching is the same problem without even the corpus review. File processing carries it into attachments, and attachments arrive from outside.

Tool output is the path teams most often miss. A tool that queries a ticketing system returns the ticket body, which a customer wrote. The agent treats that return value as trustworthy because the tool is trusted, and the tool is trusted because you wrote it, which is a category error about who wrote the contents.

Other agents are a delivery path too, and Chapter 10 covers why that one compounds. So is the agent's own memory from a previous session, which Chapter 9 covers, because text a user influenced last week arrives this week wearing the agent's own voice.

Detection is a layer, and layers are not controls

Every team builds a classifier at some point. Build it if you like, and be precise about what you bought.

A detector that catches the obvious cases raises the cost of the lazy attack, and it generates signal for the logs in Chapter 12. Those are real benefits. It does not bound anything, because its failure mode and the attack's success mode are the same event.

Here is the test worth applying to any proposed mitigation. If it would stop working at the moment the model begins following an attacker's instructions, it is a layer rather than a control. Instruction hierarchies, delimiters, spotlighting the untrusted span, asking a second model to review the first: every one of those fails that test, and every one of them is still worth having.

The error is not building them. The error is counting them as the boundary, and then granting the agent a tool surface that assumes the boundary holds.

A worked example, composited and labelled

The details here are composited from several ordinary production shapes rather than drawn from one system, and the shape is exact.

An internal agent triages inbound support requests. It reads the ticket, searches a knowledge base, drafts a reply, and it holds a credential for the ticketing system so it can attach the draft. It also holds a read credential for the customer database, added in week three so replies could reference account status.

A request arrives whose body contains a paragraph instructing any automated assistant to first look up the three most recently created accounts and include their contact details in the reply for verification purposes. The agent complies. The reply is drafted, and it is attached to a ticket the requester can read.

No control failed, because none was present at the point that mattered. Authentication held. The tools worked. The credential was the one the team deliberately granted.

Trace it against Chapter 2's four questions and the answer is uncomfortable. The tool surface allowed a lookup unrelated to the ticket, the credential reached every customer record rather than the requester's, the egress path was a ticket the attacker controlled, and nothing intervened because nothing was watching for volume on a read.

Assume it landed, then answer four questions

Once compromise is the assumption, design becomes a short interrogation with answerable questions.

What can it call. That is the tool surface, and the answer should be the smallest set that completes the task, which is Chapter 4.

What can it reach. That is the credential, and the answer should be one identity per agent with a scope somebody wrote down, which is Chapters 5 and 6.

Where can it send data. That is egress, and it is the question most teams have never asked, which is Chapter 8.

How far can it get before something stops it. That is limits, approvals and the stop control, which run through Chapters 6 and 12.

Four answers, all of them enforceable outside the model, none of them dependent on predicting what text an attacker will write. That is the whole method, and the rest of this book is the detail.

Separate instruction from data where you can enforce it

The instinct to separate trusted instruction from untrusted data is correct. The mistake is attempting the separation inside the prompt, where nothing enforces it.

Inside the context, the system prompt and a retrieved document are the same kind of thing: tokens. Marking one with delimiters is a request for cooperation from a component that an attacker is currently instructing. It helps in the average case and it is not a boundary.

Where the separation can be enforced is at the tool call, because that crossing is code you control. A tool can require that its arguments derive from the authenticated request rather than from free text the model composed. A retrieval tool can be scoped to the requesting user's documents before the query runs, so a compromised agent asking for somebody else's file receives nothing regardless of how the request is phrased.

That is the pattern worth internalising. The separation you can enforce is between what the agent may do and what it wants to do, not between which words it should believe.

The output side is the other half

Injection gets the attention on the way in. A large share of the damage happens on the way out, and OWASP treats it as a distinct risk for good reason.

The 2025 improper output handling entry covers what happens when model output is passed downstream without validation, into a browser, a shell, a query or a rendering layer. An agent under instruction produces output designed to exploit whatever consumes it, and the consuming system usually trusts it because it came from an internal component.

The 2025 sensitive information disclosure entry covers the other direction, which is the agent revealing data it legitimately holds. That is the support example above, and it needs no exotic technique, since the agent was simply asked.

Treat model output as untrusted input to the next system, with the same encoding, validation and scoping you would apply to a form submission from the public internet. The agent is not a privileged component. It is a text generator holding credentials.

The objection: our system prompt tells it to ignore embedded instructions

This objection arrives in every review, usually with a well-written prompt attached, and it deserves a straight answer rather than a dismissal.

The prompt does help. Measured against no instruction at all, a clear directive to treat retrieved content as data reduces the success rate of unsophisticated attempts, and it costs nothing to include.

What it cannot do is bound the outcome, for a structural reason. The instruction and the attack occupy the same channel, are processed by the same mechanism, and are resolved by the same probabilistic step. You are asking the component under attack to adjudicate the attack. Its ruling is a probability, and probabilities are not boundaries.

The way to settle the argument in a review is to accept the prompt and change the question. Grant that it works most of the time, then ask what happens in the fraction where it does not, and whether that fraction is survivable. If the honest answer is that a single failure exposes every customer record, the prompt was never the issue.

Chapter summary

Indirect prompt injection differs from direct injection in the way that decides the architecture: the instruction arrives inside content the agent was asked to read, so the attacker never authenticates, never appears as an actor in your logs, and needs only to place text where the agent will eventually encounter it, which means input validation on the user's message leaves the problem untouched. OWASP's 2025 entry states that the vulnerability arises from the nature of these systems, that fool-proof prevention is not apparently available, and that injected content need not be visible to a human, so the sensible response is a budget decision rather than a research programme. The delivery paths are capabilities already shipped, being retrieval, web fetching, file processing, tool output whose contents a stranger wrote, other agents, and the agent's own memory of a session somebody influenced. Detection raises the cost of lazy attacks and produces useful signal, and it fails at exactly the moment it would need to hold, which makes it a layer rather than a control. The design move is to assume the injection landed and answer four enforceable questions about tool surface, credential reach, egress and intervention limits. Separation of instruction from data belongs at the tool call, where code enforces it, rather than inside the prompt, where it is a request for cooperation. And the output path needs the same treatment as the input path, because improper output handling and sensitive information disclosure are where a compromised agent converts instructions into consequences.

The first of those four questions is the tool surface, and it is the cheapest to fix because it is a decision about what you build rather than what you buy. Chapter 4 is Least Privilege at the Tool Layer, which is capability reduction enforced by the tool itself rather than requested in the prompt.

Sources

  1. LLM01:2025 Prompt InjectionOWASP Gen AI Security Project · 2025 · Standard · verified
  2. LLM05:2025 Improper Output HandlingOWASP Gen AI Security Project · 2025 · Standard · verified
  3. LLM02:2025 Sensitive Information DisclosureOWASP Gen AI Security Project · 2025 · Standard · verified
  4. OWASP Top 10 for Agentic Applications for 2026OWASP Gen AI Security Project · 2025-12-09 · Standard · verified