Direct vs Indirect Prompt Injection: The One That Matters Is the One You Never Typed
4 min read · updated August 3, 2026
Both are prompt injection and both use the same mechanism. Only one of them is a security incident, and teams routinely spend their budget on the other.
Two shapes, one mechanism
The mechanism is identical in both cases: text that arrived as data gets read as instruction. What differs is who put the text there and who pays when it works. That single distinction is worth more than any taxonomy of payloads, because it tells you whether you are looking at a product-policy problem or at an attacker with a victim.
| Distinction | Description |
|---|---|
| direct | The person typing the prompt is the person trying to bend it. They are attacking a session they already own. The harm lands on your policy, your brand or your bill — not on another user. |
| indirect | The instruction is embedded in content the model reads on the user's behalf — a document, a web page, an email, a code repository, a tool result. The user did not write it and cannot see it. The harm lands on them. |
The academic framing arrived with Greshake, Abdelnabi and colleagues in 2023 (“Not what you’ve signed up for: Compromising Real-World LLM-Integrated Applications with Indirect Prompt Injection”, presented at the ACM AISec workshop). The paper’s contribution was not a clever payload; it was the observation that retrieval and tool use turn any content the model can reach into an input channel with no authentication on it.
Direct: the user attacks their own session
A user pastes something intended to make your assistant ignore its instructions, adopt a persona, produce content your policy forbids, or reveal how it was configured. This is real and it is worth work, but be precise about the actual loss:
- Policy and reputational risk. Your product produced something you would not want screenshotted. Genuine, and mostly a content-safety problem rather than an access-control one.
- Cost. Someone turns your assistant into a free general-purpose model for their own use. That is a quota and spend-cap problem.
- Configuration disclosure. They extract your system prompt. Treat it as public from the start and this stops being an incident.
What direct injection cannot do, in a correctly built system, is give the user data or actions they were not already entitled to — because authorisation is enforced in your tool layer against their session, not by the model’s willingness. If a direct injection escalates privilege, the injection is not the bug. The missing server-side check is.
Indirect: content attacks the user
Now the shape is conventional. An attacker who has never touched your application places text somewhere your model will read it. A victim asks their assistant a perfectly reasonable question. The assistant retrieves the poisoned content, and the instructions in it execute with the victim’s privileges, inside the victim’s session, against the victim’s data.
That is a confused deputy: your agent holds authority the attacker does not have, and the attacker gets it to act. Every property that makes the direct case tolerable is inverted. The victim has no visibility — the injected text may never appear in the transcript. There is no consent. And the attack is asynchronous: the payload can sit in a wiki page for a month waiting for someone to ask about it.
It also scales in a way direct injection does not. One poisoned public page can be read by every agent in an organisation, and a payload that instructs the model to reproduce itself into whatever the agent writes next gives you propagation between contexts. Researchers have demonstrated exactly this class of self-replicating chain in responsibly-disclosed lab work against agent frameworks; the defensive takeaway is that the unit of containment must be the agent’s capabilities, since content boundaries evidently do not hold.
Enumerating the indirect surface
The useful exercise is boring: list every path by which bytes you do not control reach a context window. Most teams find more than they expected.
- Retrieved documents in a RAG index, including anything a user was allowed to upload.
- Web pages fetched by a browsing tool, plus everything they link to if the agent follows links.
- Email bodies, calendar invite descriptions and locations, and attachment text.
- Issue trackers, pull request descriptions, code comments, commit messages, dependency READMEs.
- Tool and API responses — including error strings, which are often echoed verbatim and rarely reviewed.
- Other models’ output in a multi-agent pipeline, which is untrusted the moment any upstream agent touched untrusted content.
- OCR and transcription output, where the text in an image or an audio file becomes prompt text.
- Metadata nobody thinks of as content: filenames, EXIF fields, HTTP headers, alt text, PDF annotations.
Controls that only apply to the indirect case
Because the harm and the actor differ, so do the controls. Direct injection is addressed by content policy, quotas and an assumption that the system prompt leaks. Indirect injection needs architecture:
- Provenance tracking. Know, for every span in the context, where it came from. You cannot make policy decisions about trust that you have not represented in your data model.
- Separate the reader from the actor. The context that ingests untrusted content should not be the context that holds tool credentials. Passing structured, validated results between them — rather than free text — is the practical version of that rule.
- Deny outbound side channels by default. If the agent can construct a URL that leaves your network, an injection can carry data out of it. See exfiltration via rendered links and images.
- Human approval scaled to irreversibility. Reading a document should not require a click. Sending a message, moving money, or deleting anything should.