Multi-Tenant RAG چیست؟ طراحی ACL برای جلوگیری از نشت اطلاعات بین کاربران

در RAG سازمانی، relevance بدون authorization خطرناک است. این مقاله طراحی tenant isolation، visibility، users/groups و ACL-aware retrieval را با یک تجربه واقعی بررسی می‌کن

نوشتهٔ HomAI6 دقیقه مطالعه
  • Multi Tenant RAG
  • RAG Access Control
  • RAG ACL
  • Enterprise RAG
  • Tenant Isolation
  • Vector Database ACL

Multi-Tenant RAG چیست؟ طراحی ACL برای جلوگیری از نشت اطلاعات بین کاربران

فرض کنید یک موتور RAG برای چند تیم شرکت دارید: مالی، منابع انسانی، فنی و فروش. همه از یک Search API استفاده می‌کنند، اما نباید همه اسناد برای همه کاربران قابل مشاهده باشند.

در چنین سیستمی بزرگ‌ترین خطر لزوماً پاسخ اشتباه نیست؛ ممکن است سیستم پاسخ درست را از سندی بدهد که کاربر اصلاً مجاز به دیدن آن نیست.

این مسئله باعث می‌شود ACL و tenant isolation جزو core retrieval باشند، نه یک قابلیت جانبی UI.

Multi-Tenancy در RAG یعنی چه؟

Tenant می‌تواند نماینده این موارد باشد:

  • یک شرکت در SaaS
  • یک سازمان
  • یک business unit
  • یک customer workspace

هدف این است که داده tenant A به tenant B نرسد.

در کنار tenant، داخل هر tenant نیز access policy داریم.

مثلاً:

{
  "tenant_id": "acme",
  "visibility": "private",
  "owner": "user-17",
  "allowed_groups": ["finance"],
  "allowed_users": ["user-42"]
}

چرا Filtering بعد از Search کافی نیست؟

یک طراحی ساده این است:

Search all documents
-> get top 100
-> remove unauthorized docs
-> return top 10

این روش چند مشکل دارد.

اول، ممکن است ۹۰ نتیجه اول unauthorized باشند و بعد از حذف، recall مجاز ضعیف شود.

دوم، document غیرمجاز وارد retrieval/reranking pipeline شده است.

سوم، ممکن است trace یا cache اطلاعات آن را نگه دارد.

بهتر است policy تا حد امکان هنگام candidate generation اعمال شود.

Push-down Filter

اگر backend مثل Qdrant payload filter دارد، tenant و ACL conditions باید به همان query backend اضافه شوند.

به‌طور مفهومی:

vector nearest neighbors
WHERE tenant_id = current_tenant
AND ACL allows current principal

این کار candidate space را از ابتدا محدود می‌کند.

Tenant Isolation و Global Documents

گاهی بعضی اسناد global هستند و همه tenantها اجازه مشاهده دارند.

Policy باید این حالت را صریح تعریف کند.

مثلاً:

document.tenant_id == user.tenant_id
OR document.visibility == global

در ماژول default tenant mode ایزوله طراحی شده تا tenant-scoped documentها بدون مجوز وارد tenant دیگر نشوند.

Policy دقیق باید در application تعریف شود.

باگ خطرناک: متن ثابت، ACL جدید

یکی از مهم‌ترین bugهایی که در hardening پیدا کردیم مربوط به re-ingest بود.

سناریو:

  1. سند public ingest می‌شود.
  2. بعداً administrator همان سند را private می‌کند.
  3. متن سند هیچ تغییری نکرده است.
  4. سیستم با content hash تشخیص می‌دهد source unchanged است.
  5. اگر metadata update نادیده گرفته شود، ACL قدیمی public باقی می‌ماند.

این bug نشان می‌دهد idempotency نباید فقط text-aware باشد.

قانون درست:

تغییر policy حتی با محتوای ثابت، یک تغییر معتبر source state است.

در v4 این مورد با regression test پوشش داده شد.

Users و Groups

ACL سازمانی فقط user list نیست. group-based access معمولاً مقیاس‌پذیرتر است.

مثلاً:

allowed_groups = ["finance", "audit"]

هنگام query، principal context باید شامل groupهای کاربر باشد:

user_id=user-42
groups=[finance]
tenant_id=acme

retrieval backend سپس documentهای مجاز را انتخاب می‌کند.

Owner و Visibility

یک policy متداول:

  • public: همه کاربران tenant
  • private: فقط owner یا explicitly allowed
  • group: گروه‌های مشخص
  • global: در صورت policy، همه tenantها

مهم است semantics این values در کل سیستم ثابت باشد.

اگر ingestion و backend دو برداشت متفاوت از public داشته باشند، security bug ایجاد می‌شود.

ACL در چند Retrieval Channel

Hybrid Search معمولاً چند path دارد:

Dense
BM25F
Exact
Sparse
Graph
Visual

اگر فقط Dense path ACL-aware باشد ولی Exact Index نباشد، مهاجم می‌تواند با error code یا document ID به نتیجه دسترسی پیدا کند.

تمام candidate providerها باید policy contract یکسان داشته باشند.

این موضوع مخصوص vector database نیست؛ یک مسئله معماری retrieval است.

Cache و Multi-Tenancy

Query cache می‌تواند ناخواسته منبع leakage شود.

اگر cache key فقط query باشد:

"salary policy"

و نتیجه کاربر Finance برای کاربر Engineering reuse شود، ACL bypass رخ می‌دهد.

Cache key باید context امنیتی لازم را لحاظ کند، مثل:

query + tenant + principal/group scope + retrieval version

یا cache فقط نتایج pre-ACL امن داشته باشد.

Parent/Neighbor Expansion هم باید ACL-aware باشد

فرض کنید hit مجاز است و context builder parent آن را اضافه می‌کند.

اگر parent یا neighbor policy متفاوتی داشته باشد و assembler آن را بدون check اضافه کند، leakage رخ می‌دهد.

بنابراین ACL فقط برای hit اولیه نیست؛ هر documentی که وارد final context می‌شود باید policy مناسب داشته باشد.

در طراحی production بهتر است hierarchy داخل یک source policy-consistent باشد یا expansion مجدداً authorization را enforce کند.

Delete و Revocation

فرض کنید دسترسی یک سند revoke شود یا source حذف شود.

سیستم باید مطمئن شود:

  • vector entries حذف/به‌روز شده‌اند
  • lexical index stale نیست
  • exact IDs stale نیستند
  • cache invalidate شده
  • persistence state sync است

در v4 source deletion و replacement با persistence و rollback سخت‌تر شدند تا state نیمه‌کاره کمتر ایجاد شود.

Authorization و Source Trust دو مفهوم متفاوت‌اند

ممکن است سندی کاملاً trusted باشد ولی user اجازه دیدن آن را نداشته باشد.

یا user اجازه دیدن یک upload کم‌اعتماد را داشته باشد.

پس:

ACL -> permission
Trust -> ranking/risk signal

نباید این دو را مخلوط کرد.

Auditability

در محیط سازمانی بهتر است بتوانید جواب دهید:

  • این result چرا دیده شد؟
  • کدام ACL rule اجازه داد؟
  • source متعلق به کدام tenant بود؟
  • چه ranking reasonهایی داشت؟
  • چه زمانی index شد؟

Trace و metadata مناسب debugging security incident را بسیار ساده‌تر می‌کنند.

تست‌هایی که باید داشته باشید

حداقل این caseها را تست کنید:

Cross-tenant

کاربر tenant A نباید private document tenant B را ببیند.

ACL update بدون text change

public -> private باید فوراً اعمال شود.

Group access

عضو Finance نتیجه را ببیند؛ کاربر خارج گروه نبیند.

Exact query

حتی با document ID دقیق، سند غیرمجاز برنگردد.

Context expansion

parent/neighbor غیرمجاز وارد context نشود.

Restart

ACL بعد از restart persistence همان رفتار را داشته باشد.

Separate Index per Tenant یا Shared Index؟

دو معماری رایج وجود دارد.

Index جداگانه

مزایا:

  • isolation ساده‌تر
  • blast radius کمتر

معایب:

  • operational overhead
  • tenantهای زیاد مدیریت را سخت می‌کنند

Shared Index + Payload Filter

مزایا:

  • مدیریت ساده‌تر در scale متوسط
  • resource sharing

معایب:

  • correctness ACL بسیار حساس است
  • filter bug می‌تواند شدید باشد

انتخاب به scale، compliance و معماری backend بستگی دارد.

برای سازمان کوچک با تعداد tenant محدود، هر دو قابل استفاده‌اند؛ ولی shared index فقط با test جدی ACL.

معماری پیشنهادی

Authenticated Request
  ↓
Principal Context
(user, tenant, groups)
  ↓
Query Router
  ↓
ACL-aware Dense / Lexical / Exact retrieval
  ↓
Fusion / Reranking
  ↓
Policy + Trust enforcement
  ↓
ACL-safe Context Expansion
  ↓
LLM

Authorization context باید از ابتدای request تا آخر pipeline همراه query باشد.

جمع‌بندی

در RAG سازمانی، relevance بدون permission خطرناک است.

یکی از مهم‌ترین تجربه‌های ما این بود که ACL را نمی‌توان بعداً روی Search «اضافه» کرد؛ باید از ingestion state و candidate generation تا cache، context expansion و deletion جزو معماری باشد.

اگر RAG قرار است روی اسناد واقعی سازمان کار کند، Multi-Tenancy و ACL correctness به اندازه embedding quality مهم‌اند—و در بسیاری از سناریوها مهم‌ترند.

به‌روزرسانی:

همهٔ نوشته‌ها

نوشته‌های مرتبط