Guide
RAG et droits d'accès : chacun ne voit que ses documents
Droits d'accès dans un RAG : filtrer à la recherche par groupes, pièges des bases partagées, exemples avec pgvector, Azure et LlamaIndex.
Mis à jour le
Dans un RAG d'entreprise, le contrôle d'accès se fait au moment de la recherche : chaque passage indexé porte la liste des groupes autorisés à le lire, et chaque recherche est filtrée sur les groupes de la personne qui pose la question. Le modèle ne reçoit ainsi jamais un passage interdit. Demander au modèle de « ne pas parler » d'un document qu'on lui a transmis n'est pas une protection.
Le risque, nommé par l'OWASP
L'OWASP (fondation qui publie des référentiels de sécurité applicative) consacre une entrée de son classement des risques des applications de modèles de langage aux faiblesses des vecteurs et plongements, LLM08:2025. Parmi les risques cités :
- accès non autorisé et fuite de données, quand les contrôles d'accès sont insuffisants ou mal alignés ;
- fuites d'informations entre contextes, dans les environnements où plusieurs catégories d'utilisateurs ou d'applications partagent la même base vectorielle ;
- inversion des plongements, quand un attaquant reconstitue une partie du texte source à partir des vecteurs ;
- empoisonnement des données, par des initiés ou des sources non vérifiées.
Parmi les parades recommandées : des bases vectorielles qui tiennent compte des permissions, avec un contrôle d'accès fin ; un partitionnement logique des données par niveau d'accès ; un étiquetage des données pour faire respecter ces niveaux ; et des journaux détaillés et infalsifiables des recherches.
Le principe : un filtre de sécurité à chaque recherche
Microsoft documente ce schéma pour Azure AI Search sous le nom de « security filter pattern » :
- chaque document indexé porte un champ
group_ids(liste de groupes autorisés), filtrable et non renvoyé dans les résultats ; - chaque requête applique le filtre
group_ids/any(g:search.in(g, 'group_id1, group_id2'))avec les groupes de l'utilisateur ; - seuls les documents dont un groupe correspond sont renvoyés.
Deux précisions de la documentation, valables au-delà d'Azure. D'abord, le champ de groupe n'est qu'une chaîne : il n'y a ni authentification ni autorisation dans le filtre lui-même. C'est votre application qui doit établir, de façon sûre, à quels groupes appartient l'utilisateur. Ensuite, masquer le champ dans les résultats ne protège rien : c'est l'application du filtre à chaque requête qui fait la sécurité. Microsoft signale aussi qu'Azure AI Search propose une prise en charge intégrée des listes de contrôle d'accès au niveau du document, pour les cas où elle est utilisable.
Avec PostgreSQL et pgvector
Avec pgvector, le filtre est une clause WHERE ordinaire combinée à la recherche par distance :
SELECT * FROM items WHERE category_id = 123 ORDER BY embedding <-> '[3,1,2]' LIMIT 5;
Remplacez category_id par une colonne de groupes. Point d'attention signalé par la documentation : avec un index approximatif, le filtre s'applique après le parcours de l'index, et la requête peut renvoyer moins de résultats que demandé. Depuis la version 0.8.0, les parcours itératifs (SET hnsw.iterative_scan = strict_order;) parcourent davantage l'index jusqu'à trouver assez de résultats.
Avec LlamaIndex
LlamaIndex permet de filtrer les passages par métadonnées avant la recherche des plus proches, avec des filtres du type ExactMatchFilter, transmis au moteur de requête par filters=. Le principe reste le même : la métadonnée est posée à l'indexation, le filtre est imposé par votre code, jamais choisi par le modèle.
Les pièges courants
| Piège | Conséquence | Parade |
|---|---|---|
| Droits copiés une fois à l'indexation | un départ ou un changement de poste n'est pas répercuté | resynchroniser les droits avec l'annuaire |
| Filtre choisi par le modèle | une question habile, ou un document piégé, peut l'élargir | filtre posé par le code, à partir de l'identité authentifiée |
| Cache des réponses partagé | une réponse calculée pour un directeur servie à un stagiaire | clé de cache incluant les groupes |
| Résumés ou index dérivés (graphe, résumés de communautés) | ils mélangent des documents de niveaux différents | un index dérivé par niveau d'accès, ou pas d'index dérivé |
| Journaux trop bavards | les passages confidentiels se retrouvent dans les journaux | journaliser les identifiants, pas les textes |
Exemple illustratif
Prenons une entreprise industrielle de quatre-vingts personnes avec trois niveaux : documents ouverts à tous (procédures, sécurité), documents de service (qualité, achats), documents de direction (contrats, comptes, stratégie). À l'indexation, chaque passage reçoit la liste des groupes de son dossier d'origine. À la question, l'application lit les groupes de l'utilisateur dans l'annuaire, les ajoute au filtre, et le modèle ne voit que ce que la personne pourrait ouvrir elle-même dans le partage de fichiers.
Et les données personnelles
Si vos documents contiennent des données personnelles, leur indexation pose aussi des questions relevant du règlement général sur la protection des données (RGPD). Cette page décrit une technique, pas un avis juridique : faites examiner votre projet par votre délégué à la protection des données ou par un professionnel du droit.
Questions fréquentes
Peut-on demander au modèle de ne pas révéler certains documents ?
Ce n'est pas une protection fiable. Le filtrage doit se faire à la recherche : un passage que l'utilisateur n'a pas le droit de lire ne doit jamais être transmis au modèle.
Avec pgvector, un filtre WHERE peut-il faire perdre des résultats ?
Oui : avec un index approximatif, le filtre s'applique après le parcours de l'index, et la requête peut renvoyer moins de lignes que demandé. Les parcours itératifs, disponibles depuis la version 0.8.0, corrigent ce point.
Sources
Pages consultées pour écrire cet article :