Enterprise Access Control: как построить RAG, который не сливает чужие данные

Индексация схлопывает данные разных уровней доступа в один векторный индекс. Ретривер находит самое релевантное, модель пересказывает — и операционист получает документ юротдела, к которому у него нет доступа в SharePoint.

Enterprise Access Control: как построить RAG, который не сливает чужие данные

Операционист «Ковчега» спрашивает корпоративного ассистента: «Какие условия по последней сделке с этим клиентом?» Ассистент честно ищет в RAG-индексе, находит документ из юридического отдела и отдаёт. Проблема: у операциониста нет доступа к этому документу в SharePoint. Он не взламывал систему — он просто спросил бота, а бот не субъект прав. Ретривер нашёл самое релевантное, модель пересказала. Так через ИИ происходит привилегированная утечка: данные, к которым у человека нет доступа в источнике, вытекают через ассистента.

Это фундаментальный дефект наивного RAG. Индексация схлопывает данные разных уровней доступа в один векторный индекс, а поиск по смыслу не знает про права. Ниже — как это чинится: от архитектурного паттерна до конкретного стека, шагов реализации и режимов отказа.

Бизнес-цель

Пользователь «Ковчега» получает из RAG ровно то, что ему разрешено видеть в исходных системах — не больше. Обещания бизнесу:

  1. Ни один ответ не содержит данных, к которым у спросившего нет доступа в источнике.
  2. Отзыв доступа в источнике отражается в RAG за минуты, не за сутки.
  3. На вопрос регулятора или аудита «почему этот сотрудник это увидел» ответ лежит в правах, а не в удаче ретривера.

Что именно угрожает системе

  • 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-контекст, аудит каждого доступа.

Источники

Если ваш RAG отдаёт ответ, который пользователь не мог получить в источнике напрямую, — у вас не умный ассистент, а дыра в доступе, замаскированная под чат.

Leave a Reply

Your email address will not be published. Required fields are marked *