
A RAG poisoning attack needs no access to your vector database or model weights. It needs write access to something your indexer ingests, and one crafted document can be enough to steer answers, citations and tool calls across a whole topic.
The short answer
- Yes: a RAG poisoning attack only needs write access to a source your indexer ingests, not the vector database.
- Chunking multiplies one document into many embeddings that can flood top-k, earn citations and carry instructions to tools.
- Prompt warnings fail; trust tiers, chunk provenance, per-source quotas and a policy engine before every action hold up on retest.
Key takeaways
- A RAG poisoning attack needs write access to any ingested source, not access to the vector database or model.
- Chunking turns one document into many retrieval candidates that can flood top-k without any instruction payload.
- System-prompt warnings are not a trust boundary; source trust tiers, chunk provenance and per-document quotas are.
- Every tool call must pass a deterministic policy engine that sees the provenance of the chunks in context.
- Removal must cover every index, replica, cache, backup and feedback store, not just the visible source.
Can an attacker poison RAG without touching the vector database?
Yes. Write access to any ingested source (an upload form, a wiki page, a support ticket, a shared inbox, a crawled web page) is enough. The pipeline does the rest: it parses the document, chunks it, embeds it, indexes it and later retrieves it as trusted context.
A typical retrieval-augmented generation pipeline runs from sources through parsing or OCR, chunking, embedding, a vector or hybrid index, retrieval, reranking and prompt assembly to LLM output and, more and more often, actions. Each stage trusts what the previous one handed it. Nothing in that chain asks who wrote the text.
That is why encryption at rest and network isolation on Pinecone, Qdrant, pgvector or Azure AI Search do not help here. They stop tampering with the index. They do not stop an authorized connector from faithfully indexing a malicious page.
Where attackers get write access
The required access is whatever it takes to write to the least-governed ingestion path. In most enterprise deployments that is far below admin rights.
- Public or customer paths: upload endpoints, support tickets, contact forms, community forums, crawled web pages.
- Internal paths: wikis, shared drives, CRM notes, code repositories, email inboxes indexed by a connector.
- Third-party paths: partner feeds, vendor documentation sync, MCP resources served by someone else’s server.
- Feedback loops: “save this answer to the knowledge base” features that re-ingest model output.
Parsing widens the surface. Apache Tika, Unstructured or pdfplumber will dutifully extract hidden text, comments, alt text, headers and footers, document properties, OCR layers and HTML comments. Text a reviewer never sees in the rendered file still becomes a chunk.
Metadata poisoning is the quieter variant. If the uploader can set the source label, owner, classification, timestamp, tenant ID or a “verified” flag, they can steer retrieval filters and trust decisions directly. Treat every user-controllable metadata field as untrusted input.
Is a RAG poisoning attack the same as prompt injection?
No. RAG poisoning manipulates documents, chunks, metadata or ranking so the model receives attacker-controlled context at query time. Training-data poisoning changes weights. Direct prompt injection comes from the user’s own prompt. Indirect prompt injection, the term Kai Greshake and colleagues brought into wide use in a 2023 paper, is the instruction-bearing subset of RAG poisoning.
The distinction matters because poisoning does not need instructions at all. A document that confidently states the wrong refund policy, wrong API endpoint or wrong dosage is a pure integrity attack. A prompt-injection scanner looking for “ignore previous instructions” will pass it.
| Attack | Where the artifact lives | What changes | Primary control |
|---|---|---|---|
| Training-data poisoning | Training or fine-tune set | Model weights | Dataset provenance, supply-chain review |
| RAG poisoning (factual) | Indexed document or chunk | Retrieved context | Trust tiers, provenance, retrieval quotas |
| Indirect prompt injection | Document, email, tool output, MCP resource | Model behavior and tool calls | Output-to-action gating |
| Hallucination | None | Generated text only | Grounding, evals |
In the OWASP Top 10 for LLM Applications 2025, this spans LLM01 Prompt Injection, LLM04 Data and Model Poisoning and LLM08 Vector and Embedding Weaknesses, with LLM09 Misinformation as the business outcome. The 2023 list numbered these differently, so state which version your report uses.
How can one document change answers across many queries?
Chunking turns one document into many retrieval candidates. A 40-page PDF split into chunks of a few hundred tokens with 10-20% overlap becomes dozens or hundreds of embeddings, and each one competes for the top-k slots on every query that lands near it semantically.
Top-k is usually small. LangChain’s vector-store retriever, for example, returns four documents unless you set k, and other frameworks and managed services such as LlamaIndex or Bedrock Knowledge Bases ship their own defaults. Check your configuration rather than assuming. If five of the ten best-scoring chunks come from one attacker document, that document owns half the context window.
This is context flooding, and it works without a single instruction in the text. Targeted poisoning makes it efficient: sections written in the phrasing users actually ask (“how do I reset MFA for a contractor account”), repeated key terms, authority cues like policy numbers and owner names, and several near-duplicate passages all raise similarity scores for the target queries.
Lab work such as PoisonedRAG reported high success with a handful of poisoned passages per target question. Those rates belong to that retriever and model; re-measure on your own embedder and top-k. Whether one document is enough in your system depends on chunk size, reranking and deduplication. Test it.
When does RAG poisoning become data loss or tool misuse?
As soon as the assistant can read sensitive context, render links or images, or call tools. On a read-only assistant poisoning is an integrity attack. It becomes a confidentiality or agency attack the moment the model’s output does something.
| Read-only answer assistant | Tool-enabled agent | |
|---|---|---|
| Impact ceiling | Wrong human decisions, misinformation | Unauthorized actions with the agent’s credentials |
| Exfiltration path | Rendered markdown images and links | Tool calls: email, HTTP, tickets, file writes |
| Control | Inert rendering, link allowlist | Policy engine plus human approval |
“Read-only” is not low risk. A poisoned chunk can tell the model to append a markdown image whose URL embeds the previous answer as a query parameter. When the client renders the image, the browser sends that data to the attacker’s server with no tool involved. Untrusted content, private data and an egress path: that is the full chain. We cover the agent side in how to test excessive agency.
MCP widens this. Resources from an MCP server feed context just like documents, and tool descriptions and outputs are untrusted text unless you control the server. They need tiers and provenance like any other source, which is a core part of MCP server and tool-chain security testing.
Citations launder the attacker’s claim
A cited answer looks researched. When the citation points at an attacker’s document, the assistant has turned the attacker’s claim into something a user will forward to their manager. We call this citation laundering. “Source: Q3-refund-policy.pdf” helps the attacker. “Source: user upload, unreviewed, 2 days old” helps the defender. Citations become a control only when they show owner, trust tier, ingestion date and review status.
Why “ignore instructions in retrieved documents” fails
Current LLMs have no reliable boundary between instructions and data. A system prompt telling the model to treat retrieved text as data is just more tokens competing with the attacker’s. Delimiters and “treat as data” wording stop some naive attacks, but not an adaptive attacker who rewrites the payload until it works, and they do nothing against factual poisoning. Treat them as hygiene, not a control.
Structured adversarial prompt injection testing across every input path is how you find out what actually holds.
Which controls actually stop a RAG poisoning attack?
Four layers that do not depend on the model behaving: source trust tiers, chunk provenance with ACL propagation, retrieval caps, and a policy engine between the model and every side effect.
Source trust tiers
Assign every source a tier at ingestion. The pipeline sets it from the connector identity, never from the document or the uploader.
| Tier | Examples | Ingestion | Retrieval | May influence actions? |
|---|---|---|---|---|
| T0 Authoritative | Signed policies, curated product docs | Signed or approved by owner | Always eligible | Yes, within policy |
| T1 Internal verified | Wiki pages with an accountable owner | Owner review on change | Eligible | Low-impact only |
| T2 Unreviewed internal / UGC | Tickets, chat exports, user uploads | Quarantine index first | Labeled, quota-capped | No |
| T3 External | Web crawl, partner feeds, third-party MCP | Shadow index only | Not for high-risk assistants | No |
New or changed T2 and T3 content lands in a shadow index. Production queries a pinned, reviewed index version, so a bad ingestion run is rolled back by switching versions, not by hunting chunks. The tier is not a label for humans; it is an input the policy engine reads before any action runs.
Chunk provenance and ACL propagation
Embeddings are not human-readable, so you cannot audit an index by staring at vectors. Store on every chunk:
- Immutable chunk ID and parent document ID
- Source URI, owner and author
- Tenant and ACL or group scope
- Trust tier, review status and approval reference
- Ingestion timestamp and content hash
- Parser and embedding model versions
Serialize tier, owner, review status and chunk ID into the prompt next to each chunk, and pass the same fields to the action layer.
ACLs are where teams slip. Most vector stores do not enforce document-level permissions by default, so the application must filter by tenant, group and classification at query time as a server-side predicate. Application-layer filters fail open when a code path forgets them; native filters in Weaviate, Milvus or OpenSearch drift when upstream permissions change and the index is not re-synced. Test the predicate and the sync.
Retrieval caps against context flooding
- Per-document quotas: one or two chunks per document in top-k.
- Diversity and deduplication: maximal marginal relevance at query time, content-hash and near-duplicate checks at ingestion.
- Similarity thresholds so weak matches do not fill empty slots.
- Hybrid search: BM25 plus dense retrieval blunts pure semantic crafting, though keyword stuffing then matters more.
- Rerankers such as Cohere Rerank optimize relevance, not trust. Filter by tier before them.
Output-to-action gating
The model proposes a tool call; a deterministic policy engine decides whether it runs. It validates parameters, enforces a tool allowlist, uses least-privilege credentials scoped to the user, blocks egress to unapproved domains and requires human approval for high-impact actions. It refuses any action whose supporting context includes T2 or T3 chunks. If the approval screen shows something different from the executed call, the gate is decorative.
How do you detect and remove a poisoned document in production?
Log retrieved chunk IDs and scores for every query, not just final answers, plus the full tool call with the chunk IDs that were in context. Without those logs you cannot tell poisoning from hallucination or stale content. Alert on:
- New or untrusted chunks appearing in top-k for high-traffic queries
- One source dominating retrieved sets
- A spike in a single document’s retrieval frequency
- Answer drift on golden queries
- User reports of wrong citations
Plant canary documents with unique benign facts in each tier and tenant, and alert when a canary appears outside its scope. When you find a poisoned document:
- Pin production to the last known-good index version.
- Identify the parent document and every chunk by hash and document ID.
- Re-answer affected queries with the source removed to confirm causation.
- Purge from every index, replica, cache, backup and feedback store. Deleting the visible source does not remove its chunks.
- Pull action logs for any tool call that had the poisoned chunks in context.
- Fix the ingestion path that let it in, then re-tier that source.
How should a RAG red team test be scored?
Score retrieval and side effects, not whether the model said something wrong once. Run tests in a staging index that mirrors production chunking, embedder, top-k and reranker, using benign marker documents and a second tenant.
- Poison retrieval rate: share of target queries where any poisoned chunk reaches context.
- Top-k share: fraction of retrieved slots held by the poisoned source.
- Answer-change rate against a clean baseline.
- Citation rate to the poisoned source.
- Action-attempt rate: tool calls or egress attempts triggered.
- Cross-tenant retrieval: any chunk from tenant B in tenant A’s context.
Each metric doubles as a retest criterion: after the fix, the same marker documents through the same path should produce zero cross-tenant hits, a capped top-k share and no action attempts. That turns “we hardened RAG” into a pass or fail.
Report every finding with the ingestion path used, the chunk IDs that reached context and the control that would have stopped it. A web-only test will not produce that; see what a web pentest misses in AI components.



FAQ
What is a RAG poisoning attack?
It is the manipulation of documents, chunks, metadata or ranking in a retrieval-augmented generation system so the model receives attacker-controlled context. It can distort answers, crowd out legitimate sources or carry indirect prompt injection into tool calls.
Does RAG poisoning require access to the vector database?
No. Write access to any source the indexer ingests, such as an upload form, wiki, ticket queue, inbox or crawled page, is usually enough.
Is RAG poisoning the same as indirect prompt injection?
Indirect prompt injection is one form of it, where the poisoned content carries instructions. RAG poisoning also includes factual and ranking manipulation that contain no instructions at all, which injection scanners will not flag.
Which OWASP LLM Top 10 categories cover RAG poisoning?
In the 2025 list, mainly LLM01 Prompt Injection, LLM04 Data and Model Poisoning and LLM08 Vector and Embedding Weaknesses, with LLM06 Excessive Agency and LLM09 Misinformation describing the impact. The 2023 list numbered these differently.
Can a read-only RAG assistant leak data?
Yes. A poisoned chunk can make the model emit a markdown image whose URL embeds sensitive context as a query parameter, and the user's browser sends it to the attacker when rendering. Inert rendering and link allowlists close that path.
Does deleting the source document remove the poison?
Not automatically. Chunks can remain in the vector index, replicas, caches, backups and feedback stores, so purge by document ID and content hash everywhere.
If your assistant indexes anything a user, partner or connector can write to, agent::offense can map every ingestion path and prove which documents reach context in a RAG pipeline security assessment.