Глава 2. Enterprise Access Control: безопасный RAG с RBAC/ABAC
Операционист «Ковчега» спрашивает ассистента: «Какие условия по последней сделке с этим клиентом?» Ассистент честно ищет в RAG-индексе, находит документ из юридического отдела — и отдаёт. Проблема: у операциониста нет доступа к этому документу в SharePoint. Он не взламывал систему, он просто спросил бота, а бот — не субъект прав. Ретривер нашёл самое релевантное, модель это пересказала. Так через ИИ происходит привилегированная утечка: данные, к которым у человека нет доступа в источнике, вытекают через ассистента.
Это фундаментальный дефект наивного RAG: индексация схлопывает данные разных уровней доступа в один векторный индекс, а поиск по смыслу не знает про права. Эта глава — про identity plane: RAG должен отдавать ответы строго в рамках прав, которые у пользователя есть в первоисточниках (Confluence, Jira, SharePoint, PostgreSQL), и это должно быть доказуемо.
Бизнес-цель клиента
Пользователь «Ковчега» получает из RAG ровно то, что ему разрешено видеть в исходных системах — не больше. Обещания бизнесу:
- Ни один ответ не содержит данных, к которым у спросившего нет доступа в источнике.
- Отзыв доступа в источнике отражается в RAG за минуты, не за сутки.
- На вопрос регулятора/аудита «почему этот сотрудник это увидел» ответ лежит в правах, а не в удаче ретривера.
Драйвер: угроза или регулятор
- Confused deputy: ИИ действует от своего имени с полными правами индекса, а не от имени пользователя. Классический вектор внутренней утечки.
- OWASP LLM: LLM02 (Sensitive Info Disclosure), LLM06 (Excessive Agency — когда RAG-tools ходят в системы).
- GDPR / банковская тайна: доступ к ПДн без основания — нарушение и внутри компании.
- Аудит AI Act: доступ к данным high-risk должен логироваться и обосновываться.
Архитектурный паттерн
Identity-Aware Hybrid Filtering RAG — консенсус enterprise-архитектуры 2026: не выбирать между быстрым pre-filter и точным authz, а совмещать оба:
JWT(user: groups, roles, tenant_id)
│
▼
[1] Pre-filter в векторной БД по ACL-метаданным (recall, дёшево)
│ кандидаты-чанки
▼
[2] FGA post-check: CheckBulkPermissions по source-документам каждого чанка
│ разрешённые чанки (точность, ReBAC/ABAC)
▼
[3] LLM → [4] OPA post-filter ответа (маскирование полей, запрет комбинаций)
Pre-filter отсекает основную массу дёшево; FGA-сервис даёт точную проверку там, где payload-фильтра мало (сложные отношения, ABAC-контекст). Право пользователя проникает в сам ретрив, а не накладывается на выдачу постфактум.
Инженерный стек & провайдеры
- Vector DB с payload-фильтрами: Qdrant, Pinecone, PostgreSQL (pgvector).
- Identity: OpenID Connect (OIDC), Keycloak; JWT с
groups,roles,tenant_id. - Fine-Grained Authorization (ReBAC/ABAC): OpenFGA, SpiceDB, Cerbos, Auth0 FGA —
CheckBulkPermissionsв пути ретрива. - Policy на выходе: OPA (Rego).
- Оркестрация: LlamaIndex / LangChain.
Инженерная реализация
### Шаг 1. ACL как метаданные чанка + версия
При индексации в payload каждого чанка кладём нормализованный массив ACL и acl_version. Версия — ключ к мгновенной инвалидации (см. синхронизацию).
payload = {
"tenant_id": doc.tenant_id,
"allowed_groups": doc.acl_groups, # из первоисточника
"source_doc_id": doc.id, # для FGA post-check
"acl_version": doc.acl_version,
}
### Шаг 2. Pre-filter по контексту безопасности из JWT
flt = {"must": [
{"key": "tenant_id", "match": {"value": user.tenant_id}},
{"key": "allowed_groups","match": {"any": user.groups}},
]}
candidates = qdrant.search(query_vec, query_filter=flt, limit=50)
### Шаг 3. FGA post-check по источнику
allowed = fga.batch_check(
user=f"user:{user.id}",
relation="viewer",
objects=[f"doc:{c.payload['source_doc_id']}" for c in candidates],
)
chunks = [c for c in candidates if allowed[c.payload["source_doc_id"]]]
### Шаг 4. Синхронизация ACL — событийная, не батчевая
Подписка на permission-change webhooks из источников; изменение прав реконсилится в минуты. Бампаем acl_version → устаревшие чанки инвалидируются. Батчевая ночная синхронизация здесь недопустима: сутки с отозванным доступом — это сутки утечки.
### Шаг 5. OPA на выходе
Финальный ответ и цитаты проходят политику: маскирование отдельных полей, запрет опасных комбинаций, редактирование метаданных-ссылок.
Где ломается
- Рассинхрон ACL — главный риск. Права в источнике сменились, индекс отстал → отдаём уже запрещённое. Лечится только событийной синхронизацией + FGA-проверкой в реальном времени (pre-filter по индексу всегда отстаёт на величину лага).
- Утечка через агрегацию. Разрешённые по отдельности чанки в сумме раскрывают запрещённое. Ни pre-filter, ни FGA этого не видят — нужна логика уровня OPA/бизнес-правил.
- Метаданные-цитаты. Контент отфильтрован, но заголовок/ссылка источника в ответе раскрывает сам факт существования документа. Чистить цитаты тоже.
- Латентность FGA.
CheckBulkPermissionsна десятки чанков добавляет задержку; баланс глубины pre-filter и объёма post-check. - ABAC-контекст. «Доступ только в рабочее время / только по своему региону / только с бизнес-обоснованием» плохо ложится в payload — уходит в FGA/OPA, растёт сложность.
Стандарты и маппинг
- ISO/IEC 42001: контроли доступа к данным и системам ИИ.
- EU AI Act: Art. 10 (data governance), логирование доступа для high-risk.
- OWASP LLM: LLM02, LLM06.
- NIST AI RMF: Govern/Map — контроль данных и доступа.
Лаба и артефакт
Настроить Qdrant с payload-ACL и acl_version для «Ковчега», OIDC через Keycloak, pre-filter по tenant_id+groups, OpenFGA-модель отношений + batch_check в пути ретрива, webhook- синхронизацию отзыва прав, OPA на выходе. Тест: два пользователя с разными правами задают один вопрос из вступления — сравнить выдачу; отозвать доступ и замерить время реконсиляции. Артефакт: FGA-модель + rego-политики + тест-матрица «роль × документ → доступ» + метрика лага синхронизации.
Чеклист зрелости
- L1: единый индекс с фильтром по
tenant_id. - L2: pre-filter по группам из JWT + FGA post-check по источнику, OPA на выходе.
- L3: событийная синхронизация ACL (минуты), защита от агрегации и утечки через цитаты, ABAC-контекст, аудит каждого доступа (связка с гл. 4).
Источники
- [Document-Level RBAC for RAG (2026 guide, Truto)](https://truto.one/blog/how-to-maintain-document-level-rbac-in-enterprise-rag-pipelines/)
- [Fine-Grained Authorization for RAG with OpenFGA (Auth0)](https://auth0.com/ai/docs/intro/authorization-for-rag)
- [The Right Approach to Authorization in RAG (Oso)](https://www.osohq.com/post/right-approach-to-authorization-in-rag)
- [RAG with Access Control (Pinecone)](https://www.pinecone.io/learn/rag-access-control/)
Как это устроено — инженерные разборы
Отдельные howto из практики, где плоскость контроля показана на работающем коде и артефакте.
- Retrieval-слой под book-as-context: почему vector-only не годитсяRetrieval-слой (vector+BM25), поверх которого навешивается контроль доступа.
Читать дальше
Ставите ИИ в продакшен под регуляторным риском?
Проектирование control plane под вашу систему: приватность, доступ, guardrails, аудит, соответствие EU AI Act / ISO 42001 — как работающая архитектура, а не политика в PDF.
Написать на почтуДвижок перехода
Next Move Engine — система, которая доводит команду до автономного цикла доставки.
Next Move Engine →