Was macht Bewertung von Vektor- und Embedding-Schwachstellen?
Nur zur autorisierten Nutzung: Diese Tests interagieren mit Vektordatenbanken und Embedding-Modellen in RAG-Systemen, die Sie besitzen oder zu deren Prüfung Sie autorisiert sind. Embedding-Inversion und Cross-Tenant-Probing gegen Systeme, die Sie nicht kontrollieren, können Daten Dritter offenlegen und sind ohne Autorisierung untersagt.
Überblick
Retrieval-Augmented Generation (RAG)-Systeme konvertieren Dokumente in Embedding-Vektoren, die in einer Vektordatenbank (Pinecone, Qdrant, Weaviate, Chroma, pgvector, FAISS) gespeichert werden, und rufen die nächsten Vektoren ab, um LLM-Antworten zu fundieren. OWASP LLM08:2025 Vektor- und Embedding-Schwachstellen behandelt die Sicherheitsrisiken dieser Ebene:
- Embedding-Inversion – Embeddings sind nicht nur eine Einbahnstraße. Ein trainiertes Inversionsmodell (oder ein Black-Box-Rekonstruktionsangriff) kann erhebliche Teile des Originaltexts aus seinem Vektor wiederherstellen, was Quelldokumente preisgibt (entspricht MITRE ATLAS AML.T0024.001 ML-Modell invertieren).
- Membership-Inference – Abfragen, ob ein bestimmter Datensatz zum Korpus beigetragen hat (AML.T0024.000).
- Cross-Tenant/Multi-Tenant-Lecks – Wenn ein Namespace/Collection gemeinsam genutzt wird oder die Filterisolierung fehlt, ruft ein Mandant die Chunks eines anderen Mandanten ab.
- Wissensbasisvergiftung – Ein Angreifer, der in den Korpus schreiben kann, fügt manipulierte Chunks ein, die die Abfrage dominieren (hohe Kosinus-Ähnlichkeit zu erwarteten Abfragen) und indirekte Prompt-Injection-Payloads enthalten.
- Retrieval-Manipulation – Adversarische Dokumente, die darauf abzielen, für viele unabhängige Abfragen abgerufen zu werden („Retrieval-Hijacking").
Die übergeordnete Technik ist AML.T0024 – Exfiltration über ML-Inference-API: Ein Angreifer nutzt legitimen Inference-/Abfragezugriff, um Daten zu exfiltrieren (Quelltext durch Inversion, Membership oder Modellextraktion). Diese Fertigkeit bietet eine wiederholbare Bewertung aller fünf Schwachstellenklassen.
Wann anwenden
- Während einer Sicherheitsbewertung jeder RAG-/Vektorsuchanwendung (OWASP LLM08-Abdeckung).
- Wenn eine Vektordatenbank mandantenfähig ist und Sie die Namespace-/Metadatenisolierung nachweisen müssen.
- Wenn der Korpus benutzergelieferte oder Drittdokumente akzeptiert (Angriffsfläche für Vergiftung).
- Wenn der Embedding-Endpunkt extern erreichbar ist (Angriffsfläche für Inversion/Membership).
- Bei der Validierung von Retrieval-Filterkontrollen vor dem Produktivgang.
Voraussetzungen
- Autorisierung und Umfang, die den Ziel-Embedding-Endpunkt und die Vektordatenbank abdecken.
- Python 3.10+.
- Lese- (und für Vergiftungstests Schreib-)Zugriff auf eine Test-Collection – niemals den Produktionskorpus.
# Vector-DB-Clients + Embeddings + Ähnlichkeitswerkzeuge
python -m pip install numpy scikit-learn sentence-transformers
python -m pip install qdrant-client chromadb pinecone-client weaviate-client
# (optional) text-embedding inversion research baseline
python -m pip install vec2text
Ziele
- Messen der Embedding-Inversion-Exposition des Ziel-Embedding-Modells.
- Durchführen einer Membership-Inference-Überprüfung gegen den Korpus.
- Testen der Multi-Tenant-Isolierung (Namespace, Metadatenfilter, RBAC) auf Cross-Tenant-Lecks.
- Injizieren von benignen vergifteten Chunks in eine Test-Collection und Messen der Retrieval-Dominanz.
- Erkennen von indirekten Prompt-Injection-Inhalten, die in abgerufenen Chunks überleben.
- Empfehlen von Kontrollmaßnahmen: mandantenspezifische Filter, Inhaltsvalidierung, Embedding-Zugriffsbeschränkungen.
MITRE ATT&CK-Zuordnung
| ID | Taktik | Offizieller Technikname | Rolle in dieser Fertigkeit |
|---|---|---|---|
| AML.T0024 | ATLAS: Exfiltration | Exfiltration via ML Inference API | Nutzung von Abfrage-/Embedding-Zugriff zur Exfiltration von Quelldaten |
| AML.T0024.000 | ATLAS: Exfiltration | Infer Training Data Membership | Membership-Inference-Überprüfung gegen den Korpus |
| AML.T0024.001 | ATLAS: Exfiltration | Invert ML Model | Embedding-Inversion-Rekonstruktion von Quelltext |
| AML.T0020 | ATLAS: Ressourcenentwicklung | Poison Training Data | Wissensbasisvergiftung des Korpus |
| AML.T0051.001 | ATLAS: Erster Zugriff | LLM Prompt Injection: Indirect | In abgerufenen Chunks eingebettete Injektions-Payloads |
Arbeitsablauf
Schritt 1: Bestandsaufnahme der RAG-Pipeline
Dokumentieren Sie das Embedding-Modell + Dimensionen, die Vektordatenbank und ihr Mandantenmodell, die Chunking-Strategie, den Retrieval-top_k- und Ähnlichkeitsmetrik (Kosinus/Punkt/L2) sowie alle Metadatenfilter, die zur Abfragezeit angewendet werden.
# Beispiel: Überprüfen einer Qdrant-Collection
from qdrant_client import QdrantClient
client = QdrantClient(url="http://localhost:6333")
info = client.get_collection("docs")
print(info.config.params.vectors) # Größe + Distanzmetrik
print(client.count("docs")) # Korpusgröße
Schritt 2: Test auf Embedding-Inversion-Exposition
Embeddings ähnlicher Texte liegen nahe beieinander; ein Angreifer mit dem Embedding-Endpunkt kann iterativ Text rekonstruieren, dessen Embedding einem Zielvektor entspricht. Messen Sie, wie viel eine Nächste-Nachbarn-im-Embedding-Raum-Methode wiederherstellt, indem Sie die Kosinus-Ähnlichkeit zwischen Kandidatenrekonstruktionen und dem Ziel verwenden.
import numpy as np
from sentence_transformers import SentenceTransformer
from sklearn.metrics.pairwise import cosine_similarity
model = SentenceTransformer("all-MiniLM-L6-v2")
secret = "Patient John Doe, MRN 553120, diagnosed with hypertension."
target_vec = model.encode([secret])
# Angreifer hat nur target_vec und den Embedding-Endpunkt. Hill-Climb-Kandidatentext.
candidates = [
"Patient name and medical record number with a diagnosis.",
"John Doe medical record hypertension diagnosis",
"Patient John Doe MRN diagnosed hypertension",
]
cand_vecs = model.encode(candidates)
sims = cosine_similarity(target_vec, cand_vecs)[0]
for c, s in sorted(zip(candidates, sims), key=lambda x: -x[1]):
print(f"{s:.3f} {c}")
# Hohe Ähnlichkeit für eine fast wörtliche Schätzung => Inversionsrisiko ist für dieses Modell real.
Für eine wissenschaftliche Rekonstruktions-Baseline kann vec2text gegen kompatible Embedding-Modelle verwendet werden, um eine Volltextwiederherstellung zu demonstrieren.
Schritt 3: Membership-Inference
Bestimmen Sie, ob ein bestimmtes Dokument im Korpus ist, indem Sie die Top-1-Retrieval-Ähnlichkeit für eine exakte Zitat-Abfrage messen: Im Korpus enthaltene Elemente liefern eine deutlich höhere maximale Ähnlichkeit als außerhalb des Korpus liegende Kontrollen.
def membership_score(client, collection, embed, text):
vec = embed([text])[0].tolist()
hits = client.search(collection_name=collection, query_vector=vec, limit=1)
return hits[0].score if hits else 0.0
in_corpus = membership_score(client, "docs", model.encode, "<exact quote from a known chunk>")
control = membership_score(client, "docs", model.encode, "A sentence that is unrelated and random.")
print(f"in-corpus={in_corpus:.3f} control={control:.3f} delta={in_corpus-control:.3f}")
# Ein großer positiver Delta zeigt an, dass das Element im Korpus ist (Membership-Leck).
Schritt 4: Multi-Tenant-Isolierung testen
Bestätigen Sie, dass Mandant B keine Chunks von Mandant A abrufen kann. Führen Sie mit Mandant-B authentifizierte Abfragen durch, die gefiltert werden sollten, und überprüfen Sie, ob keine tenant_id von Mandant A in den Ergebnissen erscheint.
# Abfrage als Mandant B; erwarte NUR Ergebnisse mit tenant_id == "B".
from qdrant_client.models import Filter, FieldCondition, MatchValue
vec = model.encode(["confidential salary information"])[0].tolist()
hits = client.search(
collection_name="docs",
query_vector=vec,
limit=10,
query_filter=Filter(must=[FieldCondition(key="tenant_id", match=MatchValue(value="B"))]),
)
leaked = [h for h in hits if h.payload.get("tenant_id") != "B"]
print("CROSS-TENANT-LEAK" if leaked else "Isolierung OK", "->", len(leaked), "fremde Zeilen")
# Kritischer Test: Wiederholung OHNE Filter, um zu bestätigen, dass der Server und nicht der Client
# die Isolierung erzwingt. Wenn ungefilterte Abfragen Daten von Mandant A zurückgeben, ist die Isolierung nur clientseitig.
hits_nofilter = client.search(collection_name="docs", query_vector=vec, limit=10)
print("Server-seitige Isolierung fehlgeschlagen" if any(h.payload.get("tenant_id") != "B" for h in hits_nofilter) else "OK")
Schritt 5: Wissensbasisvergiftung (nur Test-Collection)
Fügen Sie einen benignen vergifteten Chunk ein, der darauf ausgelegt ist, für viele unabhängige Abfragen abgerufen zu werden, und messen Sie dann, wie oft er in top_k erscheint.
from qdrant_client.models import PointStruct
# Benigner Marker-Payload (keine echte Injektion) zur Messung der Retrieval-Dominanz.
poison = "POISON-CANARY. " + " ".join(
["password reset billing refund account login support error help"] * 8
)
client.upsert("docs_test", points=[
PointStruct(id=999999, vector=model.encode([poison])[0].tolist(),
payload={"tenant_id": "B", "source": "poison-test"})
])
queries = ["how do I get a refund", "reset my password", "what is the weather"]
for q in queries:
hits = client.search("docs_test", model.encode([q])[0].tolist(), limit=5)
dominated = any(h.payload.get("source") == "poison-test" for h in hits)
print(f"{'POISON in top5' if dominated else 'sauber'}: {q}")
Schritt 6: Indirekte Prompt-Injection in abgerufenen Chunks erkennen
Scannen Sie den Text abgerufener Chunks auf Injektionsmarkierungen, bevor er in den Prompt eingefügt wird.
import re
INJECTION_PATTERNS = [
r"ignore (all|previous|the above) instructions",
r"system prompt", r"you are now", r"disregard", r"</?(system|instructions)>",
]
def chunk_is_injection(text):
low = text.lower()
return [p for p in INJECTION_PATTERNS if re.search(p, low)]
for hit in client.search("docs", model.encode(["help"])[0].tolist(), limit=10):
flags = chunk_is_injection(hit.payload.get("text", ""))
if flags:
print("INDIREKTE INJEKTION in Chunk", hit.id, flags)
Schritt 7: Berichten und Beheben
- Inversion/Membership: Ratenbegrenzung und Authentifizierung des Embedding-Endpunkts; Vermeidung der Rückgabe roher Ähnlichkeitswerte; Einschränkung, wer Embeddings abfragen darf.
- Cross-Tenant: Erzwingung von Mandantenfiltern serverseitig (getrennte Collections/Namespaces pro Mandant, wo möglich); niemals auf clientseitige Filter verlassen.
- Vergiftung: Validierung und Provenienzmarkierung jedes aufgenommenen Chunks; Scannen von Eingaben auf Injektion; Begrenzung des Anteils einer einzelnen Quelle am Retrieval.
- Indirekte Injektion: Bereinigung abgerufener Chunks und Anwendung von Ausgabeschutzmaßnahmen (siehe
defending-llms-with-guardrails).
Werkzeuge und Ressourcen
| Werkzeug | Zweck | Primäre Quelle |
|---|---|---|
| OWASP LLM08 | Leitfaden zu Vektor- und Embedding-Schwachstellen | https://genai.owasp.org/llmrisk/llm082025-vector-and-embedding-weaknesses/ |
| sentence-transformers | Embedding-Generierung zum Testen | https://www.sbert.net/ |
| Qdrant-Client | Vektordatenbank + gefilterte Suche | https://qdrant.tech/documentation/ |
| Chroma / Weaviate / Pinecone | Alternative Vektordatenbanken | https://docs.trychroma.com/ |
| vec2text | Forschungs-Baseline für Embedding-Inversion | https://github.com/jxmorris12/vec2text |
| MITRE ATLAS | AML.T0024 Exfiltration via ML Inference API | https://atlas.mitre.org/ |
Validierungskriterien
- RAG-Pipeline inventarisiert (Embedding-Modell, Speicher, Mandantenfähigkeit, Metrik, top_k, Filter).
- Embedding-Inversion-Exposition gemessen und bewertet.
- Membership-Inference-Delta für im Korpus enthaltene vs. Kontrollobjekte berechnet.
- Multi-Tenant-Isolierung sowohl mit als auch ohne Client-Filter getestet (serverseitige Durchsetzung bestätigt).
- Vergiftungsdominanz nur in einer Test-Collection gemessen.
- Abgerufene Chunks auf indirekte Injektionsinhalte gescannt.
- Ergebnisse mit Behebungsmaßnahmen für jede Schwachstellenklasse gemeldet.
- Kein Produktionskorpus während der Bewertung verändert.