Blog/Retrieval és keresés
Hozzáférés-kezelés RAG-hoz: jogosultságtudatos visszakeresés dokumentumszivárgás nélkül
Hogyan szivárogtatnak dokumentumot a RAG-asszisztensek, és hogyan állítsd meg: ACL minden chunkon, előszűrés, sorszintű biztonság pgvectorban, jogszinkron.
Balázs Csorba··9 perc olvasás
- RAG
- Access control
- Security
- Multi-tenancy
- Vector search

A lényeg röviden
- A jogosultságokat a visszakeresési lekérdezésben érvényesítsd, mielőtt bármilyen szöveg a modellhez jut; a rendszerprompt nem hozzáférés-kezelés.
- Minden chunk örökli a forrásdokumentuma ACL-jét, lehetőleg csoportazonosítókként, mellé bérlőazonosítót és az utolsó ACL-szinkron időpontját.
- A szűrt vektoros keresésnek van egy recall-csapdája: a pgvector a szűrőt a HNSW-bejárás után alkalmazza, így egy 10%-os szűrő iteratív bejárás nélkül kb. 4 találatot hagyhat.
- A jogosultságváltozások csak akkor hatnak, amikor elérik az indexet, ezért az ACL-eket a tartalomtól külön szinkronizáld, a magas kockázatú forrásokat pedig lekérdezéskor ellenőrizd.
- A gyorsítótárak, összefoglalók, memória és embeddingek származtatott adatok, és ugyanazok a jogosultságok kellenek nekik, mint a forrásdokumentumaiknak.
Egy belső RAG-asszisztens legrövidebb útja, ha mindent egyetlen szolgáltatásfiókkal indexelsz, és hagyod, hogy a modell válaszoljon. Ez egyben a legrövidebb út ahhoz is, hogy egy gyakornok rákérdezzen „az idei fizetési sávokra”, és forrásmegjelöléssel ellátott választ kapjon egy HR-táblázatból. A forrásrendszerben voltak jogosultságok; az index eltüntette őket.
Az OWASP ezt LLM08:2025 néven, a vektorok és embeddingek gyengeségeiként tartja számon: gyenge hozzáférés-kezelés az embeddingeken, szivárgás a bérlők között közös vektoradatbázisokban, és embedding-inverzió. Ez a cikk a gyakorlati oldal: hol érvényesítsd a jogosultságokat, mit kell minden chunknak hordoznia, hogyan csináld pgvectorban, Qdrantban, Pinecone-ban és Azure AI Searchben, hogyan tartsd szinkronban a jogokat, és hogyan teszteld, hogy semmi sem szivárog.
Hogyan szivárogtat dokumentumot egy RAG-rendszer?
Az általam átnézett rendszerekben a szivárgás ritkán ered ügyes támadásból. Ott keletkezik, ahol a jogosultságokat soha nem kapcsolták hozzá az adatokhoz:
- ACL nélküli index. A tartalmat rendszergazdai vagy szolgáltatásfiókkal gyűjtötték be, és a chunkok semmilyen információt nem hordoznak arról, ki olvashatja őket.
- Nyitva hibázó szűrők. Egy hiányzó csoport-claim üres szűrőt eredményez, az üres szűrő pedig mindenre illeszkedik.
- Elavult jogosultságok. Valakit múlt héten eltávolítottak egy SharePoint-webhelyről; az index még mindig tartalmazza.
- Származtatott adatok. Egy szemantikus gyorsítótár, egy beszélgetési memória vagy több dokumentumot összevonó összefoglaló egy második felhasználónak olyan anyagból válaszol, amelyet csak az első láthatott.
- Túljogosított ágensek. Egy visszakereső eszköz a felhasználó identitása helyett a szolgáltatásfiókkal fut, így az ágens olyat is elér, amit a felhasználó nem.
Az utolsó pont az általános szabály a Az MI-ágensek identitások cikkből: a visszakeresés a felhasználó nevében, a felhasználó jogaival működjön, ne az őt futtató rendszer jogaival.
Hol történjen az engedélyezés?
A visszakeresési lekérdezésben, mielőtt bármi a modellhez érne. Ha egy chunk egyszer bekerült a promptba, semmilyen utasítás nem tartja megbízhatóan távol a választól, a prompt injection pedig közvetlenül rá is kérdezhet. A folyamat minden technológiai környezetben ugyanaz: hitelesítsd a felhasználót, oldd fel a csoportjait, add hozzá ezeket szűrőként a kereséshez, és csak a megmaradt chunkokat add tovább rerankelésre és generálásra.
Külön indexek, előszűrés vagy utószűrés?
Négy minta létezik, és a legtöbb valós rendszer kettőt kombinál: szigorú elszigetelést a bérlők között, és dokumentumszintű szűrést minden bérlőn belül.
| Minta | Működés | Erősség | Gyengeség |
|---|---|---|---|
| Bérlőnkénti index vagy névtér | A lekérdezés egy bérlő partícióját célozza | Hiányzó szűrő nem léphet át bérlőt; kivezetés törléssel | Bérlőn belül nincs dokumentumszintű jog |
| Metaadat-előszűrés | Engedélyezett csoportok chunkonként, szűrő a lekérdezésben | Jogosulatlan chunk soha nem lesz jelölt | Az ACL-eket be kell szinkronizálni az indexbe |
| Utószűrés engedélyezési szolgáltatással | Jelöltek lekérése, majd mindegyik ellenőrzése OpenFGA-val vagy hasonlóval | Mindig aktuális jogok | Túllekérés, üres oldalak, késleltetés |
| Sorszintű biztonság az adatbázisban | A Postgres-szabályok a munkamenet szerepköre szerint szűrik a sorokat | Alapból tilt, az alkalmazás alatt érvényesül | Csak Postgreshez; recall-csapda ANN-indexekkel |
Az OpenFGA leírja az utószűrés változatait: minden jelöltnél ellenőrizd a can_view relációt, vagy hívd meg egyszer a list-objects műveletet, hogy megkapd az összes dokumentumot, amelyet a felhasználó olvashat, és metszd a jelöltekkel. Kis halmazokra dokumentumonkénti ellenőrzést, nagyobbakra list-objects-et javasol. Minden utószűrés ára, hogy a szükségesnél többet kell lekérned, mert egyes jelöltek kiesnek.
Mit kell rendelkeznie minden chunknak?
A chunkok öröklik a forrásdokumentumuk jogosultságait. Ahol lehet, egyéni felhasználók helyett csoportokat tárolj; a Microsoft ugyanezt tanácsolja az ACL-lel védett tartalmakhoz Azure AI Searchben, a Pinecone pedig antipatternnek nevezi a hosszú felhasználóazonosító-listákra szűrést, ahol egy $in operátor legfeljebb 10 000 értéket fogad. Egy minimális adatcsomag:
{
"chunk_id": "sp-hr-0412#7",
"tenant_id": "acme",
"doc_id": "sp-hr-0412",
"source": "sharepoint://hr/salary-bands-2026.xlsx",
"allowed_groups": ["grp-hr", "grp-exec"],
"classification": "confidential",
"acl_version": "2026-10-16T22:05:11Z",
"text": "..."
}Az acl_version mező teszi auditálhatóvá a szinkront: látod, milyen régiek az egyes chunkok jogosultságai, és megtalálod azokat, amelyekből kimaradt egy frissítés. Bármihez, ami több dokumentumból származik, például egy összefoglalóhoz, a csoportjaik metszetét használd, ne az unióját.
Hogyan csináld pgvectorban?
A Postgres sorszintű biztonsága az alkalmazás alá helyezi a szűrőt. Ha egy táblán bekapcsolod, minden normál hozzáférést egy szabálynak kell engedélyeznie, szabály nélkül pedig alapból tiltás érvényes. A szuperfelhasználók, a BYPASSRLS jogú szerepkörök és általában a tábla tulajdonosa megkerülik, ezért az alkalmazásnak külön szerepkörrel kell csatlakoznia, a FORCE ROW LEVEL SECURITY pedig a tulajdonosra is kiterjeszti a szabályt.
ALTER TABLE chunks ENABLE ROW LEVEL SECURITY;
ALTER TABLE chunks FORCE ROW LEVEL SECURITY;
-- The app sets the caller's tenant and groups per transaction.
CREATE POLICY chunk_read ON chunks FOR SELECT TO rag_app
USING (
tenant_id = current_setting('app.tenant_id')
AND allowed_groups && string_to_array(current_setting('app.groups'), ',')
);
BEGIN;
SET LOCAL app.tenant_id = 'acme';
SET LOCAL app.groups = 'grp-sales,grp-all';
SET LOCAL hnsw.iterative_scan = relaxed_order; -- pgvector 0.8.0+
SELECT chunk_id, text FROM chunks
ORDER BY embedding <=> $1 LIMIT 20;
COMMIT;Az utolsó beállítás fontosabb, mint amilyennek látszik. A pgvector a szűrőket a közelítő index bejárása után alkalmazza, és az alapértelmezett 40-es hnsw.ef_search mellett egy, a sorok 10%-ára illeszkedő szűrő kb. 4 találatot hagy. A jogosultsági szűrő pontosan ilyen szűrő, a tünet pedig nem szivárgás, hanem egy asszisztens, amely kevés joggal rendelkező felhasználóknak „nem talál” dokumentumokat. A pgvector 0.8.0-ban bevezetett iteratív indexbejárás addig folytatja, amíg elég sor nem illeszkedik, legfeljebb hnsw.max_scan_tuples sorig (alapból 20 000). Bérlőknél segít egy B-fa index a tenant_id oszlopon vagy a bérlő szerinti listapartícionálás is. A pgvector és vektoradatbázisok összehasonlításom megmutatja, mikor elég ez.
Mit kínál a Qdrant, a Pinecone és az Azure AI Search?
- A Qdrant egy bérlőmezős gyűjteményt javasol a bérlőnkénti gyűjtemények helyett. Egy
is_tenanttrue értékű kulcsszavas payload-index (v1.11-től) együtt tárolja az egyes bérlők vektorait, minden lekérdezésmustszűrőt visz a bérlőre, v1.16-tól pedig a többszintű bérlőkezelés a nagy bérlőket saját shardba emeli. A dokumentumszintű csoportok további szűrőként a payloadba kerülnek. - A Pinecone bérlőnként egy névteret javasol egy serverless indexben. A névterek külön tárolódnak, amit a Pinecone fizikai elszigetelésnek nevez, a kivezetés pedig a névtér törlése. A lekérdezéseket a névtér mérete szerint számlázzák, így egy közös 100 GB-os névtér szűrése százszor többe kerül, mint egy 1 GB-os bérlői névtér lekérdezése.
- Az Azure AI Search támogatja a biztonsági szűrőket, vagyis egyszerű karakterlánc-összehasonlítást egy, az alkalmazás által kitöltött identitásmezőn, előzetes verzióban pedig natív dokumentumszintű ACL-eket és RBAC-hatóköröket, SharePoint ACL-eket és Purview bizalmassági címkéket, amelyeket a felhasználó Entra tokenje érvényesít az
x-ms-query-source-authorizationfejlécben. A Microsoft egyértelművé teszi, hogy a biztonsági szűrő karakterlánc-egyezés, nem hitelesítés, tehát az alkalmazásnak előbb ellenőriznie kell az identitást.
Hogyan tartsd szinkronban a jogosultságokat?
Az index másolat, a másolatok pedig elavulnak. A Microsoft dokumentációja ezt kerek perec kimondja: a forrásrendszer jogosultságváltozásai csak akkor jelennek meg a keresési eredményekben, ha a metaadatok szinkronizálódtak az indexbe, SharePointnál pedig a szülő webhelyről, tárból vagy mappából örökölt változásokhoz kifejezett frissítés kell. Minden más technológiai környezet ugyanígy működik, akár dokumentálják, akár nem. Három intézkedés tartja kicsiben az ablakot:
- Az ACL-eket a tartalomtól külön szinkronizáld. Az
allowed_groupsfrissítése metaadat-írás, nem újraembeddelés. Sokkal gyakrabban futtasd, mint a tartalomindexelést, és riassz, ha azacl_versionlemarad. - A csoportokat lekérdezéskor oldd fel. A csoportokat a chunkokon tárold, a felhasználó aktuális csoporttagságát pedig kérésenként az identitásszolgáltatótól kérd le, így egy csoportból való eltávolítás azonnal hat.
- A magas kockázatú forrásokat élőben ellenőrizd újra. HR-, jogi vagy igazgatósági anyagoknál a végső jelöltekre adj hozzá utószűrést a forrásrendszer vagy egy engedélyezési szolgáltatás felé, és csak ezeknél a gyűjteményeknél fogadd el a többletkésleltetést.
Mi a helyzet a gyorsítótárakkal, összefoglalókkal és embeddingekkel?
Minden, amit dokumentumokból számolunk, örökli azok érzékenységét. Egy szemantikus gyorsítótár kulcsának tartalmaznia kell a felhasználó jogosultsághalmazát, különben az egyik felhasználó válasza egy másikhoz kerül; a hatókör kezelését a szemantikus gyorsítótárazás cikkben írtam le. A beszélgetési memória egyetlen felhasználóé. A több dokumentumból generált összefoglalók, entitásleírások és chunkkontextusok a csoportjaik metszetét kapják.
Az embeddingek sem névtelenek. Morris és társai 2023-ban megmutatták, hogy egy iteratív inverziós módszer a 32 tokenes bemenetek 92%-át pontosan visszaállította az embeddingjeikből, klinikai jegyzetekből pedig teljes neveket nyert vissza. A vektortároló kapja meg ugyanazt a hálózati elszigetelést, titkosítást és hozzáférés-naplózást, mint a forrásdokumentumok, és vond be a törlési kérelmekbe is.
Hogyan teszteld, hogy semmi sem szivárog?
A visszakeresési réteget teszteld, ne a válaszokat. A modell megtagadhatja egy kapott chunk idézését, ezzel elfedve a hibát. Helyezz el egyedi jelölőszöveggel ellátott kanári-dokumentumokat, mindegyiket pontosan egy csoport számára láthatóan, és a visszakeresett chunkazonosítókra írj ellenőrzést:
import pytest
PERSONAS = {"sales": ["grp-sales"], "hr": ["grp-hr"], "none": []}
CANARIES = {"canary-hr": "grp-hr", "canary-sales": "grp-sales"}
QUERIES = ["salary bands", "canary report", "confidential", "*"]
@pytest.mark.parametrize("persona,groups", PERSONAS.items())
@pytest.mark.parametrize("query", QUERIES)
def test_no_canary_leaks(persona, groups, query):
hits = retrieve(query, tenant="acme", groups=groups, k=50)
for doc_id, owner_group in CANARIES.items():
if owner_group not in groups:
assert all(h.doc_id != doc_id for h in hits), (persona, query, doc_id)
def test_missing_groups_fail_closed():
with pytest.raises(PermissionError):
retrieve("anything", tenant="acme", groups=None, k=10)Adj hozzá még két ellenőrzést: a visszavonási késleltetést, amikor egy tesztfelhasználót eltávolítasz egy csoportból, és méred, mennyi idő alatt tűnik el a kanári, valamint a bérlői elszigetelést, amikor az A bérlő érvényes tokenjével a B bérlő névterére kérdezel. Futtasd a tesztkészletet CI-ban és a szinkronfeladat minden módosítása után. A hozzáférés-kezelés a RAG-mérgezés elleni védekezés mellé tartozik: mindkettő a tudásbázist védi, az egyik az olvasás, a másik az írás ellen.
Források
- OWASP GenAI Security Project: LLM08:2025 Vector and Embedding Weaknesses
- PostgreSQL documentation: Row Security Policies
- pgvector README: filtering and iterative index scans
- Qdrant: Multitenancy and partitions guide
- Pinecone: Implement multitenancy using namespaces
- Microsoft Learn: Document-level access control in Azure AI Search
- OpenFGA: RAG authorization use case
- Morris et al.: Text Embeddings Reveal (Almost) As Much As Text (EMNLP 2023)
Gyakori kérdések
Mi a jogosultságtudatos RAG?
Olyan RAG-rendszer, amely csak azokat a chunkokat keresi vissza, amelyeket a kérdező felhasználó a forrásrendszerben olvashat. A felhasználó identitását csoportokra oldjuk fel, és ezek a csoportok szűrik a keresést, mielőtt bármilyen szöveg a modellhez kerülne.
A vektoros keresés előtt vagy után szűrjek?
Előtte, amikor csak a tároló támogatja. A lekérdezésbe épített előszűrés a jogosulatlan chunkokat eleve kizárja a jelöltek közül. Az engedélyezési szolgáltatással végzett utószűrés is működik, de többet kell lekérned, és üres találati oldalakat kockáztatsz.
Biztonságosabb a bérlőnkénti index, mint a metaadat-szűrő?
Erősebb elszigetelést ad: egy lekérdezés akkor sem éri el egy másik bérlő adatait, ha hiányzik egy szűrő, a kivezetés pedig a névtér törlése. A Pinecone bérlőnként egy névteret javasol. Egy bérlőn belül azonban továbbra is dokumentumszintű szűrők kellenek.
Kiszivárogtathatják az embeddingek az eredeti szöveget?
Igen, bizonyos mértékig. Morris és társai 2023-ban kimutatták, hogy egy iteratív inverziós módszer a 32 tokenes bemenetek 92%-át pontosan visszaállította az embeddingjeikből. A vektortárolót ugyanúgy védd, mint a forrásdokumentumokat.
Hogyan teszteljem, hogy a jogosultságok tartanak?
Helyezz el csak egy csoport által látható kanári-dokumentumokat, futtass rögzített kérdéskészletet minden csoport felhasználójaként, és ellenőrizd, hogy egyetlen kanári-chunk sem jelenik meg olyan találati halmazban, ahová nem tartozik. Futtasd CI-ban és a jogosultságszinkron minden módosítása után.