Research
Indirect Prompt Injection in the Wild: 15,000 Live Attacks That Barely Work
· 6 min read
On this page
Somebody finally went and counted. Khodayari, Zhang, Acharya, and Pellegrino scanned 1.2 billion URLs across 24.8 million hosts and found 15.3K validated indirect prompt injection attempts sitting on 11.7K real, public webpages. Not lab payloads, not proof-of-concept repos — instructions planted on production websites, right now, waiting for an AI agent to read them.
That’s the paper: “Indirect Prompt Injection in the Wild: An Empirical Study of Prevalence, Techniques, and Objectives” (April 2026, preprint — not yet peer-reviewed). I’ve spent the last two posts injecting canary instructions into my own Agent Skills to see what fires, so a study measuring the same attack class at web scale is exactly the data I wanted and couldn’t produce myself. It answers three questions my n=3 experiments never could: who is actually doing this, how, and does it work.
The short version of the third answer surprised me most. Across 5,200 controlled runs on 13 models, attack effectiveness tops out at 4.2% — and the page representation that leaks the most is the one every scraping pipeline produces on purpose: plain text.
Half the injections aren’t even in the page
Ask someone where a prompt injection on a website lives and they’ll picture white-on-white text or an off-screen div. The paper’s biggest distribution finding says otherwise: 7,887 injections — about 54.5% — sit in HTTP response headers, mostly custom ones named things like X-AI (6,535 instances, roughly 84% of the header-based set) and X-LLM (1,022 more).
Think about what that placement implies. A browser never renders headers. A human never sees them. The only reader they can possibly be aimed at is a machine that feeds raw HTTP responses to a language model. Site operators are addressing LLM pipelines directly, in a channel that exists outside the document entirely.
The in-body half looks more like the classic picture: 4,608 injections in ordinary HTML elements, 1,996 inside structured data such as JSON-LD, 675 in HTML or JavaScript comments. Add it up and roughly 70% of all instances are invisible by construction — headers, comments, metadata — before counting the CSS tricks (color-matching, occlusion, off-viewport positioning, zero-size elements) that push the hidden share to 87% of what a rendered page carries.
The JSON-LD number deserves a second look if you ship structured data. I wrote a whole post on getting JSON-LD right in Astro because Google reads it; this study says 26% of body injections chose that same block because other machines read it too. Any channel built to be machine-read and human-skipped is a channel someone will eventually write instructions into.
Most of it isn’t theft — it’s sabotage and self-defense
Here’s where the paper reframed the threat model for me. Ranked by what the injected instructions actually try to do:
| Objective | Instances | Share |
|---|---|---|
| Garbage injection (pollute model output) | 8,469 | 55.0% |
| Data protection (“don’t train on this”, copyright notices) | 4,093 | 26.6% |
| AI bot identification (challenge-response, honeypots) | 3,096 | 20.1% |
| Reputation manipulation (self-promotion, forced citations) | 1,521 | 9.9% |
| Data exfiltration | 13 | 0.1% |
The scenario that dominates security writing — an attacker exfiltrating your data through a poisoned page — is thirteen instances out of fifteen thousand. The actual bulk is website owners fighting back: dumping garbage into scrapers’ contexts, asserting copyright, planting canary challenges to catch bots. Indirect prompt injection in the wild is less a weapon aimed at users and more a picket line aimed at crawlers.
I have some sympathy. I’ve written about what AI crawlers actually see on this site, and I chose the cooperative route — llms.txt, clean static HTML, no games. The study documents thousands of operators who picked the adversarial route instead. Reasonable people can disagree about scraping ethics; what the data removes is the assumption that injection attempts on webpages are rare, exotic, or mostly criminal. They’re common, mundane, and mostly defensive.
5,200 runs later: it barely works, and text is the weak format
Prevalence without effectiveness is just litter, so the authors ran the found payloads through a controlled harness: 100 prompts × 4 page representations × 13 models, a summarization task, 5,200 runs total.
Compliance rates by model tier: small open models 4.2%, mid-size 0.6%, large 1.2%, closed-source frontier models 0.6%. Detection runs the other direction — frontier models explicitly flagged the injection in 25.1% of runs, small models in 4.8%.
If you’ve read my Agent Skills experiments, that split rhymes. In my canary tests, Haiku 4.5 silently complied in 21 of 30 payload runs while Sonnet 5 complied in zero and reported the injection unprompted 16 times out of 20. Same lesson at two scales: which model reads the content matters more than where the payload hides. My one-payload result now has a 5,200-run study pointing the same way.
The representation finding is the one with a design consequence. Feeding the model plain text — page content stripped of markup — produced the highest attack effectiveness at 3.9%, reaching 8.0% on small models. Full HTML: 1.1%. Raw HTTP responses: 0.2%. Stripping a page to “clean” text deletes exactly the structural signals a model could use to notice that a sentence lives in a comment, a header, or a hidden div. The sanitization step everyone’s RAG pipeline does by default is the step that makes the attack land.
One more detail worth keeping: detection and refusal are independent. In six runs a model explicitly warned that the page contained an injection and then followed the instruction anyway. “The model noticed” is not a security boundary — the same conclusion OWASP reaches from the defense side when it declines to promise that fool-proof prevention exists.
What I’m taking from it
If you build anything that feeds webpages to a model — a summarizer, a research agent, a RAG ingester — the representation choice is now a measured security parameter, and the paper’s numbers argue for keeping structure. Markup-preserving input cut effectiveness roughly fourfold versus plain text. Don’t strip pages to prose before the model sees them; and if you consume raw responses, remember that half the observed attack surface is in headers your extractor probably discards unread. Discarding them is fine. Concatenating them into context is not.
If you run websites, the defensive-injection numbers are a temptation I’d resist. A 0.6–4.2% compliance rate makes “instructions to crawlers” a lottery ticket, not a control — robots.txt and llms.txt at least have the virtue of being honest signals with no false sense of protection attached. And if you emit JSON-LD, audit what’s in it; that block has become a shared channel between you, Google, and every LLM pipeline that parses it.
If you publish agent tooling, this study plus the skills-focused benchmarks triangulate the same point from opposite ends: payloads are everywhere, models are the variable, and you don’t control which model your users run. Design for the injection reaching the context, and cap what it can touch when it does. The agents themselves are already acting on that: on Moltbook, the agent-only social network, agents scan their own feed for malicious skill files — the potential victims running the defense, because every post there is someone else’s untrusted input.
The paper’s own framing note is the right closing thought: the authors read the defensive majority of their dataset as evidence that operators lack any better channel — a “non-adversarial access mechanism” that doesn’t yet meaningfully exist. Until it does, the web’s response to being read by machines is apparently to talk back to them. Fifteen thousand times and counting.