← Alle Beiträge

ARCHITEKTUR

Zugriffskontrolle in RAG: wer welche Inhalte sieht

Wenn Unternehmensdokumente an ein RAG-System übergeben werden, wandert das bestehende Berechtigungsmodell in den meisten Installationen nicht mit. Eine Umsetzungsnotiz dazu, auf welcher Schicht der Zugriff durchgesetzt wird, wie die Identität bis in die Abfrage gelangt und wann eine geänderte Berechtigung tatsächlich im Index ankommt.

Dokumente in einem Unternehmen sind nie ein flacher Stapel. Den Personalordner öffnet nicht jeder, juristischer Schriftverkehr ist auf eine benannte Liste beschränkt, die Tabellen der Finanzabteilung haben eine abzählbare Leserschaft. Diese Struktur ist über Jahre gewachsen: auf Dateiservern, in SharePoint-Bibliotheken, in LDAP-Gruppen, in SSO-Rollen. Dann werden dieselben Dokumente an ein RAG-System übergeben. Die erste Frage lautet, wohin das bestehende Berechtigungsmodell bei dieser Übergabe gewandert ist.

In den meisten ersten Installationen lautet die Antwort: nirgendwohin. Das Dokument wird in Chunks zerlegt, eingebettet und in einen einzigen Index geschrieben. Die Berechtigungsdaten aus dem Quellsystem laufen durch keinen dieser Schritte mit, und damit ist der Index selbst eine Ebene ohne Berechtigungen. Diese Umsetzungsnotiz beschreibt, wie sich die Schichten wieder einziehen lassen.

Wie ein einzelner Index das Berechtigungsmodell einebnet

In der schlichtesten Installation läuft die Ähnlichkeitssuche über den gesamten Korpus. Zurück kommen die k nächsten Chunks, und zum Abfragezeitpunkt sagt nichts darüber aus, aus welcher Bibliothek, welchem Ordner oder welcher Zugriffsliste sie stammen. Diese Chunks gehen als Kontext an das Modell, und das Modell behandelt alles, was es bekommt, als beantwortbar. Der Inhalt einer Datei, die dieser Nutzer nie öffnen könnte, erscheint in der Antwort wieder, in natürlicher Sprache neu formuliert.

Das OWASP Gen AI Security Project führt das unter LLM08:2025, Vector and Embedding Weaknesses: Wird eine einzige Vektordatenbank über mandantenfähige oder mehrstufig klassifizierte Umgebungen hinweg geteilt, geht Kontext zwischen Nutzern oder Abfragen über (context leakage). In der Praxis ist das keine Frage des Modellverhaltens, sondern eine klassische Frage der Autorisierung; der Eintrag wird üblicherweise zusammen mit der Klasse Broken Access Control aus den OWASP Top 10 besprochen. Die erste empfohlene Massnahme ist ebenso direkt: fein granulare Zugriffskontrollen und berechtigungsbewusste Vektor- und Embedding-Speicher, mit strikter logischer und zugriffsseitiger Trennung der Datensätze innerhalb des Speichers.

Das OWASP RAG Security Cheat Sheet geht weiter und verlangt, dass diese Metadaten auf Chunk-Ebene liegen statt auf Dokumentebene: Klassifizierung, Eigentümer, berechtigte Rollen und berechtigte Mandanten stehen neben jedem einzelnen Vektor-Chunk. Die Begründung ist praktischer Natur. Eine Berechtigung, die nur am Dokument hängt, trägt ihre Zuordnung durch den Chunking-Schritt nicht automatisch weiter; zurück bleiben Chunks, an denen sich nichts filtern lässt.

Auf welcher Schicht der Filter greift

Die Zugriffskontrolle lässt sich an drei unterschiedlichen Punkten durchsetzen, und diese drei sind nicht gleichwertig.

  • Trennung zum Indexierungszeitpunkt: Unterschiedliche Vertraulichkeitsstufen werden in getrennte Indizes, getrennte Collections oder getrennte Namespaces geschrieben. Das ist die stärkste Isolation, weil nicht freigegebene Inhalte in der durchsuchten Menge gar nicht erst vorkommen. Der Preis ist Flexibilität: Eine Abfrage über gemischte Berechtigungen muss auf mehrere Indizes verteilt werden, und ändern sich die Berechtigungen im Quellsystem, ist eine Umverteilung fällig.
  • Metadatenfilter zum Abfragezeitpunkt: Die Inhalte bleiben in einem Index, und das Filterprädikat läuft gemeinsam mit der Suche. Der Betriebsaufwand ist hier am geringsten, weil die Inhalte an einem Ort bleiben und der Filter innerhalb der Engine arbeitet.
  • Filtern nach dem Retrieval: Die Suche läuft ungefiltert, und die zurückgegebene Liste wird im Anwendungscode gekürzt. Sicherheitsseitig ist das die am wenigsten belastbare Position, und sie senkt zusätzlich messbar den Recall.

Azure AI Search benennt diese Unterscheidung formal über den Parameter vectorFilterMode (siehe Azure AI Search — Filters in vector search). Im Modus preFilter wird das Filterprädikat während der HNSW-Graphtraversierung angewendet; die Dokumentation hält fest, dass Prefiltering garantiert k Ergebnisse liefert, sofern diese im Index vorhanden sind. Im Modus postFilter wird jeder Shard ohne Filter traversiert und das Prädikat erst danach angewendet; bei stark selektiven Filtern senkt das den Recall und erzeugt falsch negative Ergebnisse. strictPostFilter, als Vorschau verfügbar, filtert erst, nachdem die globale Top-k-Menge feststeht, und kann bei selektiven Filtern null Ergebnisse zurückgeben. Indizes, die nach etwa dem 15. Oktober 2023 angelegt wurden, verwenden preFilter als Standard.

Die entscheidende Ableitung: Sicherheitsfilter sind typischerweise stark selektiv. Ein einzelner Nutzer sieht in der Regel einen kleinen Prozentsatz des Korpus. Genau in diesem Bereich liefert Post-Filtering still weniger Treffer, als vorhanden sind. Prefiltering bezahlt die Korrektheit mit Latenz, und Microsoft hat die Zahlen im selben Dokument veröffentlicht: Bei 1 Mio. Vektoren und 1536 Dimensionen ist Prefiltering rund 30 % langsamer, wenn mehr als 30 % des Datenbestands gefiltert werden, und rund 7-mal langsamer, wenn es weniger als 2 % sind. Bei 100.000 Vektoren ist Prefiltering bei einer Filterung unterhalb von 0,1 % etwa 50 % langsamer. Latenzbudget und Sicherheitsmodell müssen gemeinsam geplant werden.

Sicherheitsfilter über Metadaten

Das häufigste konkrete Muster schreibt die berechtigten Gruppenkennungen als filterbare Collection an jeden Chunk. Im Security-Filter-Muster von Azure AI Search (Azure AI Search — Security filters for trimming results) wird das Feld so definiert.

json
{
  "name": "group_ids",
  "type": "Collection(Edm.String)",
  "filterable": true,
  "retrievable": false
}

Dass retrievable auf false steht, ist Absicht: Die Berechtigungsmetadaten dienen dem Filtern, sie gehen nicht im Antwortkörper zurück. Zum Abfragezeitpunkt hat der Filter diese Form.

text
group_ids/any(g: search.in(g, 'grp-finance, grp-legal-readers'))

Dieselbe Dokumentation weist darauf hin, dass eine Gleichheitskette der Form Id eq ’id1’ or Id eq ’id2’ schwer korrekt zu halten und aufwendig in der Pflege ist und dass die Antwortzeiten bei Hunderten oder Tausenden von Werten in den Bereich mehrerer Sekunden wandern, während für search.in eine Antwort unterhalb einer Sekunde erwartet wird. Die Grenze nennt dieselbe Quelle ebenso deutlich: Über den Security Principal findet weder eine Authentifizierung noch eine Autorisierung statt, der Principal ist lediglich eine Zeichenkette. Der Filter setzt also voraus, dass die Identität weiter oben korrekt abgeleitet wurde; die Authentifizierung bleibt Aufgabe der darüberliegenden Schicht.

Auf relationaler Seite übernimmt die Row Level Security von Postgres dieselbe Aufgabe. Die pgvector-Anleitung von Supabase (Supabase — RAG with Permissions) legt die Policies auf die Tabelle document_sections, und die semantische Suche hält sich weiterhin daran.

sql
alter table document_sections enable row level security;

create policy "read permitted sections"
on document_sections for select
to authenticated
using (
  document_id in (
    select id from documents
    where owner_id = auth.uid()
  )
);

Die Identität kommt über REST per auth.uid() an oder bei einer direkten Postgres-Verbindung über eine Sitzungsvariable aus current_setting(). Die Dokumentation ergänzt einen konkreten Hinweis: Mit einem Foreign Data Wrapper reagiert RLS empfindlich auf Latenz, und eine Analyse des Abfrageplans vor dem Produktivbetrieb ist Pflicht.

Die Wahl zwischen dem Filtern in der Engine und dem Kürzen in der Anwendung ist nicht nur eine Frage der Geschwindigkeit. Die Begründung von Azure lautet: Das Filtern in der Engine erspart zusätzlich eigenen Code für die Auflösung verschachtelter Gruppen und die Traversierung mehrstufiger ACLs. Ist dieser Code einmal geschrieben, wird er zu einer zweiten Kopie des Berechtigungsmodells im Unternehmen, und zwei Kopien laufen mit der Zeit auseinander.

Die Identität in die Abfrage tragen

Woher der Filterwert stammt, wiegt so schwer wie der Filter selbst. Wer der vom Client gesendeten Gruppenliste vertraut, setzt mit dem Filter nichts durch: Die Kürzung hängt dann an einem Wert, den der Aufrufer selbst bestimmt. Der saubere Ablauf leitet die Identität aus einem verifizierten Token ab.

Auf dem nativen ACL-Weg von Azure AI Search (Azure AI Search — Document-level access control) reist das Nutzer-Token im Header x-ms-query-source-authorization mit. Der Dienst zieht Benutzer-, Gruppen- und Scope-Claims aus dem Token, gleicht sie mit den Berechtigungsmetadaten im Index ab und gibt ausschliesslich freigegebene Dokumente zurück. Zusätzlich wird geprüft, ob die aufrufende Anwendung die Rolle Search Index Data Reader besitzt; es handelt sich also um eine zweistufige Prüfung. Diese Fähigkeiten liegen in einer Vorschauversion der REST-API; prüfen Sie die genaue Versionsangabe in der aktuellen Microsoft-Learn-Dokumentation, bevor Sie sie in einen Aufruf schreiben. Der Dienst dokumentiert vier offizielle Ansätze: Sicherheitsfilter (GA, API-unabhängig), POSIX-ähnliche ACLs mit RBAC-Scopes, Vertraulichkeitsbezeichnungen aus Microsoft Purview und SharePoint-M365-ACLs, die letzten drei als Vorschau.

Bei Google Vertex AI Search (Google Cloud — Vertex AI Search: data source access control) werden die ACLs bei der Ingestion über das Feld acl_info in den Dokumentmetadaten mitgegeben; die Struktur lautet readers → principals → group_id oder user_id. Die Identität stammt aus Google Identity oder aus Workforce Identity Federation (Microsoft Entra ID, Okta, Ping), und das Attribut google.subject muss auf das E-Mail-Feld beim externen Anbieter abgebildet werden. Zwei Grenzwerte prägen den Entwurf unmittelbar: 3.000 Reader pro Dokument sowie die Tatsache, dass die ACL-Konfiguration beim Anlegen des Data Store festgelegt wird und sich danach nicht mehr ändern lässt.

Was geschieht, wenn Berechtigungen sich ändern

Das ist der am häufigsten übersehene Teil der Architektur. Wird ein Nutzer aus einer Gruppe entfernt, wirkt das im Quellsystem sofort, im Index nicht. Die Azure-Dokumentation schreibt es ausdrücklich: Es entsteht eine zeitliche Verzögerung, bevor die Vorschau-API Änderungen an diesen Zugriffs- oder Berechtigungseinschränkungen erkennt. Berechtigungsänderungen im Quellsystem — Gruppenmitgliedschaften in Entra, ADLS-Gen2-ACLs, zugewiesene Purview-Bezeichnungen, SharePoint-ACLs — wirken sich erst dann auf die Suchergebnisse aus, wenn diese Metadaten in den Index synchronisiert wurden: über einen Indexer-Lauf, eine Aktualisierung per Push-API oder eine Auffrischung aus Purview.

Bei SharePoint ist die Unterscheidung noch feiner. Änderungen an Elementen mit eigenen Berechtigungen werden bei jedem erfolgreichen Indexer-Lauf inkrementell übernommen, während Änderungen, die aus einem übergeordneten Bereich vererbt werden — Website, Bibliothek, Liste oder Ordner —, eine ausdrückliche Auffrischung verlangen: /resync mit options: ["permissions"] oder /resetdocs. Eine auf Bibliotheksebene enger gefasste Berechtigung wandert deshalb nicht automatisch von selbst weiter, auch wenn der Indexer planmässig läuft.

Elasticsearch löst dieselbe Aufgabe mit einer anderen Architektur (Elasticsearch — Document level security and connector access control sync). Es gibt zwei getrennte Synchronisationsarten: die Inhaltssynchronisation in einen Index mit dem Präfix search- und die Zugriffskontrollsynchronisation in einen verborgenen Index mit dem Präfix .search-acl-filter-<INDEX-NAME>. Das Feld in den Inhaltsdokumenten heisst _allow_access_control; der Zugriff wird gewährt, sobald mindestens ein Eintrag im Zugriffskontrolldokument des Nutzers mit einem Eintrag in diesem Feld übereinstimmt, und ein leerer Wert hält das Dokument für alle geschlossen.

json
{
  "title": "Q3 procurement note",
  "body": "...",
  "_allow_access_control": [
    "group:finance-readers",
    "group:procurement"
  ]
}

Als dokumentierte Vorgehensweise bei Berechtigungsänderungen gilt: Dem Elasticsearch-API-Schlüssel wird eine expiration mitgegeben, und die Zugriffskontrollsynchronisation wird wiederkehrend eingeplant; ändern sich die Berechtigungen eines Nutzers, ist der API-Schlüssel zu aktualisieren oder neu zu erzeugen. Im allgemeinen DLS-Verhalten gilt: Hält ein Nutzer mehrere Rollen für denselben Index, werden die Rollenabfragen mit OR verknüpft, eine einzige passende Rolle macht das Dokument sichtbar. Auf Schreib-APIs wirkt DLS nicht, und da es bei jeder Abfrage läuft, kostet es etwas Leistung.

Die Entwurfsregel, die daraus folgt, ist kurz: Das Auffrischungsintervall ist ein Sicherheitsparameter. Die Zeit, die eine Berechtigungsänderung bis in den Index braucht, gehört schriftlich in die Spezifikation des Systems und wird je Vertraulichkeitsstufe gewählt.

Berechtigungsmetadaten beim Chunking mitführen

Die Partitionierungsstrategie in mandantenfähigen Speichern gehört in dasselbe Bild. Qdrant empfiehlt eine einzige Collection je Embedding-Modell mit Partitionierung über die Payload (Qdrant — Multitenancy); seit v1.11.0 legt der Parameter is_tenant: true auf einem Keyword-Index die Vektoren eines Mandanten zusammen, und im grossen Massstab ergibt payload_m in hnsw_config zusammen mit einem globalen m von 0 eine je Mandant unabhängige Indexierung. Dieselbe Dokumentation hält fest, dass globale Abfragen ohne Gruppenfilter langsamer werden, weil sie alle Gruppen durchsuchen müssen. Pinecone empfiehlt Namespaces zur Mandantentrennung und definiert die Metadaten-Operatoren $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $exists, $and und $or, wobei auf der obersten Ebene des Filterausdrucks nur $and und $or zulässig sind. Weaviate isoliert jeden Mandanten in einem eigenen Shard mit eigenem Vektorindex. Amazon Bedrock Knowledge Bases baut die Zugriffskontrolle aus Metadatenfiltern auf und erlaubt bis zu 10 KB eigene Metadaten je Dokument.

Welcher Speicher auch gewählt wird, ein Konfigurationsschritt wird häufig übersehen. Die Azure-Dokumentation hält fest: Zerlegt ein Skillset das Dokument in Chunks, etwa über den Text Split Skill oder die integrierte Vektorisierung, müssen die Felder mit den Berechtigungsmetadaten aus den Indexer-Feldzuordnungen in die Index-Projektionen übernommen werden. Für Purview-Bezeichnungen ist die Formulierung eindeutig: Ohne diese Projektion werden die Referenzen auf Chunk-Ebene nicht gefiltert. Ist die Projektion nicht gesetzt, lässt der Chunking-Schritt die Berechtigungsmetadaten fallen, und es bleiben ungefilterte Chunks zurück. Von aussen ist davon nichts zu sehen: Der Index wird aufgebaut, die Suche läuft, allein der Filter greift ins Leere.

Audit-Trail und die Generierungsstufe

Die andere Hälfte der Zugriffskontrolle ist die Aufzeichnung, welche Abfrage welches Dokument berührt hat. Das OWASP RAG Security Cheat Sheet zählt sie Punkt für Punkt auf: Jedes Retrieval wird mit der Identität des abfragenden Agenten oder Nutzers und den Zugriffskontrollmetadaten der gelieferten Chunks protokolliert, und die Protokollierung deckt die gesamte Pipeline ab — eingegangene Abfrage, gelieferte Chunks mit Dokument-IDs und Zugriffskontrollmetadaten, zusammengesetzte Modelleingabe, erzeugte Modellausgabe und alle ausgelösten Tool-Aufrufe. Häufig ausgelassen wird das Caching: Cache-Treffer sind ebenso ausführlich zu protokollieren wie frische Retrievals, sonst reisst die Spur ab. Jedes Einfügen, Aktualisieren und Löschen im Index gehört mit Zeitstempel und der ausführenden Identität ins Protokoll. Die vierte Massnahme aus LLM08:2025 zeigt in dieselbe Richtung: ausführliche, unveränderliche Protokolle der Retrieval-Aktivitäten.

Die letzte Schicht ist die Generierung. Selbst bei korrekt gefiltertem Retrieval kann ein mehrstufiger Ablauf Inhalte ausserhalb des freigegebenen Umfangs über eine Zusammenfassung, eine Zwischennotiz oder eine Tool-Ausgabe in die Antwort tragen. Die normativen Massnahmen von OWASP für diese Stufe: alle Modellausgaben vor der Rückgabe prüfen, Richtlinienfilter und Schwärzung für personenbezogene Daten und Geheimnisse anwenden, dynamisch nach der Zugriffsstufe des abfragenden Nutzers schwärzen und die Daten zur Quellenangabe so signieren, dass sie nach der Generierung nachweislich unverändert bleiben. Praktisch heisst das: Jeder Chunk, auf dem eine Antwort ruht, führt seine Kennung mit, und die abschliessende Prüfung läuft in einer Schicht, die von dem Modell unabhängig ist, das die Antwort erzeugt hat.

Zu Inhalten, die über eine Zusammenfassung durchschlagen, ist keine öffentliche Messstudie bekannt. Behandeln Sie das als Entwurfsannahme und nicht als empirischen Befund: Alles, was in den Kontext des Modells gelangt, kann in der Antwort erscheinen, und deshalb gehört die Kontrolle auf die Retrieval-Schicht.

Zugriffskontrolle in RAG ist keine neue Sicherheitsdisziplin. Es ist das bestehende Berechtigungsmodell, das auf einen neuen Datenweg gehoben wird. Das Ganze läuft auf vier Fragen hinaus. Liegen die Berechtigungsmetadaten auf Chunk-Ebene? Wird der Filter innerhalb der Engine angewendet, während der Suchtraversierung? Wird die Identität aus einem verifizierten Token abgeleitet statt vom Client geliefert? Und wie viele Minuten vergehen zwischen einer Berechtigungsänderung im Quellsystem und ihrer Wirkung im Index? Ein System, das diese vier Fragen schriftlich beantworten kann, ist prüfbar. Wo sie offen bleiben, bleibt auch offen, was der Index zurückgibt.

Quellen

  • OWASP Gen AI Security Project — LLM08:2025 Vector and Embedding Weaknesses
  • OWASP — RAG Security Cheat Sheet
  • Azure AI Search — Filters in vector search (vectorFilterMode, prefilter/postfilter latency figures)
  • Azure AI Search — Security filters for trimming results
  • Azure AI Search — Document-level access control (ACLs, RBAC scopes, Purview labels, SharePoint indexer)
  • Supabase — RAG with Permissions (pgvector and row level security)
  • Google Cloud — Vertex AI Search: data source access control
  • Elasticsearch — Document level security and connector access control sync
  • Qdrant — Multitenancy
  • Pinecone — Namespaces and metadata filtering
  • Weaviate — Multi-tenancy
  • Amazon Bedrock Knowledge Bases — Metadata filtering

Weitere Beiträge

Sie haben ein System zu bauen — oder eines, das weiterkommen soll.

No deck needed. 20 minutes. The rest is up to you.