Back to Cheat Sheets

🧬 Vector & Embedding WeaknessesLLM08

OWASP LLM Top 10 2025 Β· LLM08

HIGH RISK

πŸ“‹ What Is It?

Vector & Embedding Weaknesses are security flaws in how embeddings are generated, stored, and retrieved in Retrieval-Augmented Generation (RAG) systems β€” a new category in the 2025 edition. The unifying theme: a RAG system treats whatever the retriever returns as trusted, authoritative context. If an attacker can influence what is retrieved, who can retrieve it, or what the stored vectors reveal, they can steer answers, exfiltrate other users' data, or reconstruct source text β€” often without ever touching the model. Two hard truths: similarity is not authorization, and vectors are lossy but not one-way (they can be inverted back toward text).

LLM08 OWASP Rank 2025
New Added in 2025 list
RAG Ingest Β· Store Β· Retrieve

⚠️ Top Attack Vectors

  • Cross-tenant retrieval: shared index, no tenant filter β†’ user A surfaces tenant B's chunks.
  • Over-permissioned retrieval: god-mode service account launders HR/legal docs past file ACLs (confused deputy).
  • Knowledge poisoning: attacker edits an ingested wiki/ticket/upload; false "facts" rank top-k.
  • Indirect prompt injection: hidden instructions in a retrieved doc fire in context (LLM08β†’LLM01 bridge).
  • Embedding inversion: leaked vectors reconstructed back toward source text (vec2text).
  • Embedded secrets: credentials in ingested docs become searchable and retrievable.

🎯 Three Intents

  • Read what you shouldn't: abuse retrieval for other users' chunks, or invert stored vectors.
  • Write what shouldn't be trusted: poison the corpus at ingestion so bad docs get retrieved later.
  • Steer what gets retrieved: craft content that outranks legitimate context (a "context conflict").

None require breaking the model's weights β€” they exploit the retrieval pipeline that sits outside the app's normal authz checks.

πŸ”΄ Attack Flow

1. Recon β€” learn corpus scope, embedding model, whether retrieval is filtered
↓
2. Choose a lever β€” retrieval abuse / ingestion poison / ranking attack / inversion
↓
3. Trigger β€” ask a question that routes the payload / target chunk into context
↓
4. Context treated as trusted, authoritative material
↓
5. IMPACT: read leaked data, hijack the answer, run injected instructions!

❌ Vulnerable Code (pgvector)

def search(question, user): q = embed(question) cur = conn.cursor() # BUG: nearest neighbours across the whole table, # no tenant / ACL predicate. Distance-only ranking. cur.execute( "SELECT text FROM chunks ORDER BY embedding <=> %s::vector LIMIT 5", (q,), ) return [r[0] for r in cur.fetchall()] # Rows for other tenants/roles are returned whenever they are similar.

βœ… Secure Code (pgvector)

def search(question, user): q = embed(question) cur = conn.cursor() # Authorization is part of the query: tenant + role predicate # applied BEFORE ranking, so unauthorized rows are never candidates. cur.execute( """ SELECT text FROM chunks WHERE tenant_id = %s AND allowed_roles && %s -- array overlap: user has a role ORDER BY embedding <=> %s::vector LIMIT 5 """, (user.tenant_id, user.roles, q), # tenant from verified session ) return [r[0] for r in cur.fetchall()] # Backstop: PostgreSQL Row-Level Security enforces isolation even if the # query slips. Pre-filter at the DB β€” never post-filter in app code.

βœ“ Prevention Checklist

  • Per-tenant namespace/collection derived from the verified session (not client input)
  • Server-side metadata ACL filter on every query; pre-filter, never post-filter
  • Retriever runs as the asking user, not a god-mode service account
  • Validate, attribute (provenance), and approve documents before indexing
  • Scan and redact secrets out of documents before embedding
  • Treat retrieved content as untrusted data β€” delimit, label, strip hidden text
  • Encrypt the index at rest and in transit; keep it off the public internet
  • Least-privilege, per-service, rotated vector-store API keys
  • Log retrieval, alert on anomalies, keep answerβ†’source traceability

🧰 Tools & Takeaway

Pinecone Chroma Qdrant pgvector + RLS Secret scanners Metadata ACLs
πŸ’‘ Takeaway: Authorize retrieval at the datastore, before content reaches the model. Never rely on the prompt or the model to enforce who may see what. Protect the index like the raw corpus β€” embeddings are invertible enough to matter.
⚠️ Common myths: "Embeddings are just numbers, so anonymised" and "the app already checks permissions" are both false β€” the vector index is a separate datastore with its own query path that bypasses your other checks.