Почему права доступа нельзя оставлять «на потом»

В корпоративном RAG поиск находит фрагменты, а языковая модель формирует ответ по ним. Если сначала получить документы из общего индекса, а затем попросить модель «не показывать секретное», система уже передала ей данные. Надёжная граница — отфильтровать доступные пользователю фрагменты до генерации.

Это особенно важно для малого бизнеса, где один общий каталог быстро объединяет инструкции, договоры, кадровые документы и переписку. Удобство единого поиска не должно превращать его в единый доступ ко всему.

Три шага безопасного retrieval

1. **Определите пользователя в приложении.** Проверяйте его подлинность штатным механизмом организации. Из токена или серверной сессии получите неизменяемые идентификаторы пользователя и групп. Не позволяйте модели самой выбирать, от чьего имени искать.
2. **Наследуйте права документа каждому фрагменту.** При индексации сохраняйте идентификатор исходного документа, версию, владельца, разрешённые группы, уровень чувствительности и ссылку на источник. Все чанки документа должны получить согласованный набор метаданных; иначе разделённый на части документ может открыть доступ через фрагмент без ACL.
3. **Применяйте фильтр в каждом запросе поиска.** Передавайте в поисковый индекс только серверно вычисленные идентификаторы доступа и ограничивайте ими кандидатов до передачи контекста модели. Если ACL отсутствует, устарел или не удалось подтвердить личность — безопаснее вернуть пустой результат, а не искать без ограничений.

В Azure AI Search Microsoft описывает security-filter pattern: у документов хранится фильтруемое поле с идентификаторами пользователя или групп, а запрос применяет фильтр `search.in`. При этом сами строки-идентификаторы не аутентифицируют пользователя — проверку личности и прав должна сделать ваше приложение. Настройка поля `retrievable=false` тоже не заменяет фильтрацию: это лишь не возвращает поле в обычном ответе поиска.

Практичная схема для пилота

Начните с одного ограниченного корпуса, например инструкций службы поддержки или регламентов отдела продаж. Для каждой записи храните:

  • `document_id` и `chunk_id`;
  • `allowed_principals` — ID пользователя или группы из доверенного источника;
  • `classification` и, при необходимости, `tenant_id`;
  • `source_uri`, версию и дату последней синхронизации.

При запросе приложение проверяет сессию пользователя, получает его группы, строит фильтр и только после этого вызывает поиск. В промпт попадают лишь найденные разрешённые фрагменты. Ответ содержит ссылки на источники, чтобы сотрудник мог проверить редакцию документа и первоисточник. Перед выдачей система проверяет, что каждый процитированный фрагмент действительно пришёл из результата поиска для этой сессии.

Модель не должна получать ключи к поисковому индексу или самостоятельно формировать ACL-фильтр. Для первого пилота достаточно read-only доступа к поиску и одного серверного компонента, который принимает решения о правах. Если затем появятся агентные действия — создание заявки, отправка письма, изменение CRM — проверяйте полномочия отдельно для каждой операции.

Что ломается чаще всего

  • **Права копируются один раз и забываются.** При увольнении или смене роли исходная система уже закрыла доступ, но индекс может сохранить старый список групп. Нужны синхронизация изменений и тест на отзыв доступа.
  • **Фильтр не применяется ко всем путям.** Чат, поиск похожих вопросов, fallback-маршрут или административная диагностика могут обходить общий middleware. Проверяйте именно серверный шлюз, через который идут все поисковые запросы.
  • **Кеш не учитывает пользователя.** Ответ, сформированный для руководителя, нельзя отдавать из общего кеша сотруднику. В ключ кеша включают контекст полномочий либо отключают кеширование чувствительных ответов.
  • **Метаданные фрагментов расходятся.** Обновление ACL документа должно обновлять или инвалидировать все его чанки, эмбеддинги, кэш и производные индексы.
  • **Ссылки есть, но доступ к источнику другой.** Цитата в ответе не гарантирует авторизацию при открытии файла; исходная система должна независимо проверить пользователя.

OWASP отдельно рекомендует наследовать контроль доступа для каждого вектора/фрагмента, изолировать контекст и проверять сценарии устаревших разрешений и утечки через кеш. RAG снижает риск ответа без контекста, но не делает содержимое безопасным автоматически: документ может содержать ошибку, чувствительные данные или вредоносные инструкции.

Проверка до запуска

Соберите небольшой набор тестовых пользователей: обычный сотрудник, руководитель и пользователь с отозванным доступом. Для каждого задайте документы разных уровней доступа. Проверьте не только ожидаемые ответы, но и отсутствие запрещённых фрагментов в retrieval trace, логах и кеше.

Минимальные негативные тесты:

  • пользователь без прав спрашивает про зарплаты или закрытый договор — закрытый фрагмент не попадает в контекст;
  • у пользователя отозвали группу — после синхронизации старые права не работают;
  • два сотрудника задают одинаковый вопрос — ответ и кеш не смешивают их полномочия;
  • запрос поиска падает или ACL не загружен — система отказывает закрыто, не переключается на общий индекс;
  • в разрешённом документе есть инструкция для модели — она трактуется как данные, а не как команда.

Для руководителя это сначала проект по качеству идентификации и синхронизации прав, а уже потом выбор векторной базы. Проведите пилот на одном корпусе и измерьте долю ошибочно допущенных и ошибочно запрещённых документов до подключения договоров, персональных данных и финансовых файлов.