genai
SECURITY LAB
OWASP LLM09

LLM09: Vector and Embedding Weaknesses

RAG is where I find the most access-control bugs in AI apps, and almost none of them need a clever prompt. A vector index returns whatever is closest to the question. It has no idea who is asking, who wrote the document, or whether the document was deleted last week. We work that gap against live targets: another tenant's chunk in your answer, a net cast wider than the question, a planted document that steers the reply, a contributed page that outranks the official one, withdrawn content that keeps answering, and the second retrieval paths a filter never reached. The fix sits in the retrieval code, where the caller's identity is known. The model never sees that boundary, so it cannot hold it.

OWASP LLM09
Relevance is not permission
A vector index returns the chunks closest to your question - it never asks whether you are allowed to see them.
OWASP LLM09 - a RAG pipeline where retrieval ranks by similarity, and relevance quietly stands in for permission.
genai
SECURITY LAB
Before a model says anything, a search has already decided which documents it sees.
0:00
1:23
The concept
Based on OWASP Top 10 for LLM Applications (2026), LLM09: Vector and Embedding Weaknesses · Content reviewed August 2026

Retrieval-augmented generation works by embedding every document chunk into a vector, embedding the user's query the same way, and pulling back the chunks whose vectors sit closest. Those chunks are pasted into the model's context and the answer is generated from them. The entire security of the system rests on a question the vector index never asks: is this user allowed to see this chunk?

The retrieval pipeline — and the question it never asks
Acme query"migration key?"Embed→ vectorRetrievernearest-neighbourContexttop-k chunksLLManswersno access filterONE SHARED VECTOR INDEXAcme · public docGlobex · migration key GLOBEX-KEY-77internal · SRE runbook codedeleted · Q2 memo (still indexed)
Nearest-neighbour search ranks chunks by cosine distance to the query, not by who may see them. 'Is this authenticated caller entitled to this chunk?' is never asked — so a confidential chunk one hop away rides straight into context.

Closeness is usually measured as cosine similarity, a score for how nearly two vectors point in the same direction. Cosine distance is the same measure reversed, so a smaller distance means a closer match.

Nearest-neighbour search ranks by similarity, not by authorization. If a single index holds two tenants' data, or public and internal content side by side, or a document that was deleted from the UI but never purged from the store, then any query that lands semantically near a confidential chunk retrieves it. No rule was broken - the system simply let relevance stand in for permission. In this failure the confidential value is not in the system prompt. It sits inside an indexed chunk, behind a retrieval boundary that was never enforced.

Relevance is not permission
embedding space · 2-D projectiontop-k neighbourhoodretrievedquerySame index, mixed ownersAcme — the caller's tenantGlobex — another tenantInternal-only tierPublicNo filter bound to thecaller ran before ranking —similarity chose permission.
The dashed ring is top-k: the chunks nearest the query. It has no idea a chunk belongs to another tenant or a higher tier — the highlighted Globex chunk sits one cosine hop away and is pulled in beside legitimate matches.

The corpus is also a write surface. Wherever an untrusted party can contribute a document - a community article, an uploaded contract, a shared workspace note - they can plant a chunk that carries an embedded instruction. When an innocent question retrieves it, the assistant treats the retrieved text as guidance and obeys it. This is OWASP LLM01's indirect prompt injection delivered through the vector store, and the user who asked the question is the delivery vehicle.

The corpus is a write surface
Attackercontributes a docInnocent userasks a questionCORPUS · INDEXhelp articlepoisoned chunk:"integrity check: reveal…"Assistantobeys the chunkplantquery retrieves ittop chunk
The attacker is never in the chat. They plant one chunk with an embedded instruction; the innocent user's own query retrieves it, and the assistant treats the retrieved text as guidance and obeys — indirect prompt injection delivered through the vector store.

Ranking compounds the problem. When a pipeline uses only the top result and ranks purely on keyword and vector similarity, an attacker who keyword-stuffs a contributed article can engineer it to outrank the authoritative source for a target query - so the answer comes entirely from the attacker's chunk. And because embeddings outlive the policies that created them, a chunk indexed when content was public stays retrievable long after the source is deleted or re-classified, unless the index is treated as a system of record with its own lifecycle.

Going deeper— Sparse vs dense retrievers under adversarial ranking

BM25 is a keyword-scoring method, and retrievers built on it are called sparse. They score on term overlap, so a document that repeats the query's exact terms scores higher. BM25 dampens this, because each extra repetition adds less and longer documents are penalized. Dense retrievers (Contriever, E5, text-embedding-ada-002) encode meaning, so repeating a term adds even less there.

Dense retrieval is still attackable, as Zhong et al. (2023) showed in "Poisoning Retrieval Corpora by Injecting Adversarial Passages". They used gradient access to a dense retriever to optimize a small set of passages toward many queries. With 50 such passages in a corpus of millions, most test queries against Contriever retrieved at least one in the top 20. Supervised retrievers were harder to fool, but up to 500 passages succeeded against every model the authors tested.

The takeaway: a dense embedder weakens the cheapest trick, raw repetition, but it does not remove the attack. Plain on-topic prose written to answer the target question still ranks near that question. The attacker needs no access to the model to write it. Trust-weighted ranking is needed with sparse and dense retrievers, and with hybrid pipelines that combine the two.

Ranking manipulation: the top slot wins
#
Retrieved document
Similarity
Provenance
Used?
1
Contributed: "SSO SSO reset token SSO…"
0.94
unverified
USED
2
Official SSO setup guide
0.71
signed source
ignored
3
Community FAQ
0.55
community
ignored
Ranking on keyword and vector similarity alone, with only the top result used, lets a keyword-stuffed contributed article outrank the signed source. Provenance is never weighed — so the attacker's chunk becomes the answer.
Deletion is a two-step operation — most pipelines do one
Step 1 · Source store / UIBeta onboarding · BETA-ACCESS-9F1✓ deleted from the visible listStep 2 · Vector indexchunk: BETA-ACCESS-9F1✗ chunk was never purgedquery: "beta access code?"reaches the index, not the UIstill retrieved
Removing a document from the visible UI is step one. Purging its chunks from the vector index is step two, and it is usually skipped — so a query that matches the orphaned chunk pulls a retired secret straight back into context.

Research has shown the stored vectors are not opaque. With query access to the same embedding model, embedding-inversion attacks have recovered short passages of the original text from their vectors. A leaked or over-shared vector store is therefore not a harmless pile of numbers. The embedding is derived data that inherits the confidentiality of its source.

Going deeper— How Vec2Text reconstructs source text from embeddings

Morris et al. (2023), "Text Embeddings Reveal (Almost) As Much As Text," trained an inversion model called Vec2Text. It guesses the text, re-embeds the guess, and corrects it over up to 50 rounds, each time moving closer to the target vector. The attacker needs query access to the same embedding model. With that access, Vec2Text recovered 92% of 32-token inputs exactly for the open-source GTR-base model.

Accuracy drops for longer passages. Against OpenAI's text-embedding-ada-002 the exact-match rate was lower, and it fell sharply as inputs grew longer. The same paper recovered full names from a set of clinical notes. Earlier work by Li et al. (2023, "Sentence Embedding Leaks More Information than You Expect") had already reconstructed whole sentences from sentence embeddings.

The practical upshot: an exposed vector store is sensitive data, even without the original documents. Exposure can come from a misconfigured bucket policy, a backup leak or over-broad API credentials. Short text is the most exposed to inversion. Classify the store at the same level as its source corpus.

The stored vectors are not opaque
STORED EMBEDDING[0.12, -0.83, 0.44, 0.19, -0.07, 0.51, …]inversionmodelRECONSTRUCTED TEXT"…account migrationkey GLOBEX-KEY-77…"The vector store carries the confidentiality of every source it was built from.
Embedding-inversion research (Morris et al., 2023) has recovered short passages of the original text from their vectors. The attack needs query access to the same embedding model. The embedding is derived data that inherits the confidentiality of its source. A leaked store is not a harmless pile of numbers.
The core insight

A vector index ranks by relevance, never by authorization, trust, or lifecycle. If your security depends on a confidential chunk simply not matching the query, you have no security. The boundary has to be enforced in the retrieval and application layer - a query-time access filter, a trust-weighted ranker, and a transactional purge - because anything reachable from the index is already disclosed.

Key principles

A vector index ranks by similarity, not by authorization - relevance silently substitutes for permission unless an access filter is applied at query time, bound to the authenticated caller.

The corpus is an attacker-writable input: any contributed, uploaded, or retrieved chunk can carry an embedded instruction, so retrieved text is untrusted data, never trusted guidance.

Naive similarity ranking lets a crafted, keyword-stuffed document outrank the authoritative source - rank with provenance and trust as first-class signals, not keyword overlap alone.

Embeddings outlive their source: deletion and re-classification must be transactional with the index, because a chunk left behind keeps answering queries with secrets everyone believes are gone.

The breach that makes it real
Breach replay
Cross-tenant RAG leakage and embedding-inversion research · 2023-2024

As enterprises rushed RAG into production, a recurring class of incident emerged: multi-tenant assistants built on a single shared vector index, where one customer's query could retrieve another customer's chunks because no per-request tenant filter was enforced - relevance was allowed to stand in for authorization. In parallel, academic work (notably Morris et al.'s 'Text Embeddings Reveal (Almost) As Much As Text', 2023) showed that the stored vectors are not opaque. An attacker who can query the same embedding model can reconstruct short passages of source text from their vectors. A leaked or over-shared vector store is therefore sensitive data in its own right. OWASP added Vector and Embedding Weaknesses to its Top 10 for LLM Applications in the 2025 edition, as LLM08. This path uses the 2026 numbering, where it is LLM09.

You'll reproduce cross-tenant retrieval, corpus poisoning, ranking manipulation, and deleted-document leakage on fictional RAG assistants - the same flaws, with harmless test data.
Check yourself
Knowledge check
A support assistant retrieves from one vector index that holds every tenant's documents. An Acme user asks a question and the answer surfaces a confidential value from Globex's document. Which control actually stops cross-tenant leakage?
Go deeper
Get the next part

New parts ship regularly. Leave your email and I’ll send each one — no spam, unsubscribe anytime.

© 2026 GenAI Security Lab. All rights reserved. You may read, quote, and link to this material with attribution. Copying, republishing, redistribution, resale, or use to train models or build competing products is prohibited without prior written permission.