← كل المقالات

المعمارية

التحكم في الوصول داخل RAG: من يرى ماذا

حين تُسلَّم مستندات المؤسسة إلى نظام RAG، لا ينتقل معها نموذج الصلاحيات القائم عادةً. ملاحظة تنفيذية عن الطبقة التي تفرض الوصول، وكيف تبلغ الهوية الاستعلام، ومتى يصل تغيير الصلاحية إلى الفهرس فعلاً.

المستندات داخل المؤسسة ليست كومة مسطّحة قط. فليس كل أحد يفتح مجلد الموارد البشرية، والمراسلات القانونية محصورة بقائمة مسمّاة، وجداول المالية لها قرّاء معدودون. وتلك البنية تراكمت على مدى سنوات عبر خوادم الملفات، ومكتبات SharePoint، ومجموعات LDAP، وأدوار SSO. ثم تُسلَّم تلك المستندات إلى نظام RAG. والسؤال الأول الذي ينبغي طرحه: أين ذهب نموذج الصلاحيات القائم أثناء ذلك النقل؟

وفي معظم عمليات البناء الأولى يكون الجواب: إلى لا مكان. فالمستند يُقطَّع، ويُضمَّن، ويُكتب في فهرس واحد. وبيانات الصلاحية من النظام المصدر لا تعبر أياً من تلك الخطوات، فيصير الفهرس نفسه مستوى بلا صلاحيات. وهذه ملاحظة تنفيذية عن كيفية إعادة الطبقات إلى مكانها.

كيف يسطّح الفهرس الواحد النموذج

في التركيب الساذج يجري بحث التشابه على المتن كله. وتعود أعلى k مقطعاً، ولا شيء وقت الاستعلام يقول من أي مكتبة أو مجلد أو قائمة وصول أتت. وتُسلَّم تلك المقاطع إلى النموذج بوصفها سياقاً، ويتعامل النموذج مع كل ما يُعطى له على أنه صالح للإجابة. فيعود محتوى من ملف لم يكن للمستخدم أن يفتحه إلى الظهور في الجواب، مُعاد صياغته بلغة طبيعية.

ويسمّي مشروع OWASP Gen AI Security Project هذا تحت LLM08:2025، Vector and Embedding Weaknesses: فمشاركة قاعدة بيانات شعاعية واحدة عبر بيئات متعددة المستأجرين أو متعددة الفئات تُنتج تسرّب سياق بين المستخدمين أو الاستعلامات. وعملياً ليست هذه مسألة سلوك نموذج، بل مسألة تخويل كلاسيكية، ويُناقَش البند عادةً إلى جانب فئة Broken Access Control من OWASP Top 10. وأول إجراء تخفيف موصى به فيه مباشر بالقدر نفسه: ضوابط وصول دقيقة ومخازن شعاعية وتضمينات واعية بالصلاحيات، مع تقسيم منطقي وتقسيم وصول صارمين لمجموعات البيانات داخل المخزن.

وتذهب OWASP RAG Security Cheat Sheet أبعد وتشترط أن تعيش البيانات الوصفية على مستوى المقطع لا على مستوى المستند: التصنيف، والمالك، والأدوار المسموح لها، والمستأجرون المسموح لهم، مخزّنة إلى جانب كل مقطع شعاعي. والمنطق عملي. فالصلاحية المرتبطة بالمستند وحده قد تفقد ارتباطها أثناء التقطيع، فلا يبقى إلا مقاطع بلا شيء يُرشَّح عليه.

أين يُطبَّق المرشِّح

يمكن فرض التحكم في الوصول عند ثلاث نقاط متمايزة، وهي ليست متكافئة.

  • التقسيم وقت الفهرسة: مستويات التصنيف المختلفة تذهب إلى فهارس أو مجموعات أو مساحات أسماء منفصلة. وهذا أقوى عزل، لأن المحتوى غير المصرّح به ببساطة غير موجود في المجموعة المبحوثة. والكلفة مرونة — فالاستعلام الممتد عبر صلاحيات مختلطة عليه أن يتوزّع، والتغيّرات في النظام المصدر تستدعي نقلاً.
  • ترشيح البيانات الوصفية وقت الاستعلام: المحتوى يبقى في فهرس واحد ويعمل شرط الترشيح مع البحث. وكلفة التشغيل هنا أدنى، لأن المحتوى يبقى في مكان واحد ويعمل المرشِّح داخل المحرك.
  • الترشيح بعد الاسترجاع: يجري البحث بلا ترشيح ثم تُقلَّم القائمة العائدة في كود التطبيق. وهذا أقل المواقع مناعةً من زاوية الأمان، ويُنتج كذلك فقداً قابلاً للقياس في الاستدعاء.

ويسمّي Azure AI Search هذا التمييز رسمياً عبر معامل vectorFilterMode (انظروا Azure AI Search — Filters in vector search). ففي preFilter يُطبَّق شرط الترشيح أثناء اجتياز رسم HNSW، وتنص الوثائق على أن الترشيح المسبق يضمن إعادة k نتيجة إن كانت موجودة في الفهرس. وفي postFilter تُجتاز كل شظية بلا مرشِّح ثم يُطبَّق الشرط بعدها؛ ومع المرشحات عالية الانتقائية يقلّل ذلك الاستدعاء ويُنتج سلبيات كاذبة. أما strictPostFilter، وهو قيد المعاينة، فيطبّق المرشِّح بعد إيجاد أعلى k عالمياً وقد يعيد صفر نتيجة مع المرشحات الانتقائية. والفهارس المنشأة بعد نحو 15 أكتوبر 2023 تأخذ preFilter افتراضياً.

والخلاصة المهمة هنا: مرشحات الأمان عالية الانتقائية عادةً. فالمستخدم الواحد يرى غالباً نسبة صغيرة من المتن. وهذا بالضبط هو النظام الذي يُسقط فيه الترشيح اللاحق النتائج بصمت. والترشيح المسبق يدفع ثمن الصحة زمن استجابة، وقد نشرت Microsoft الأرقام في الوثيقة نفسها: عند مليون شعاع و1536 بُعداً، يكون الترشيح المسبق أبطأ بنحو 30% حين يُرشَّح أكثر من 30% من مجموعة البيانات، وأبطأ بنحو 7 أضعاف حين يكون أقل من 2%. وعند 100,000 شعاع، يجعل الترشيح دون 0.1% الترشيحَ المسبق أبطأ بنحو 50%. فميزانية زمن الاستجابة ونموذج الأمان يجب أن يُخطَّطا معاً.

التقليم الأمني بالبيانات الوصفية

النمط الملموس الأشيع يكتب معرّفات المجموعات المسموح لها على كل مقطع بوصفها مجموعة قابلة للترشيح. وفي نمط مرشِّح الأمان لدى Azure AI Search (Azure AI Search — Security filters for trimming results) يُعرَّف الحقل هكذا.

json
{
  "name": "group_ids",
  "type": "Collection(Edm.String)",
  "filterable": true,
  "retrievable": false
}

وضبط retrievable على false مقصود: فبيانات الصلاحية موجودة للترشيح بها، لا لإعادتها في حمولة الاستجابة. ووقت الاستعلام يأخذ المرشِّح هذه الصورة.

text
group_ids/any(g: search.in(g, 'grp-finance, grp-legal-readers'))

وتحذّر الوثائق نفسها من أن بناء سلسلة مساواة مثل Id eq ’id1’ or Id eq ’id2’ بدلاً من ذلك عرضة للزلل وعسير الصيانة، وأنه مع مئات أو آلاف القيم تمتد أزمنة الاستجابة إلى ثوانٍ كثيرة، بينما يُتوقَّع لـ search.in أن يبقى دون الثانية. وتذكر الحدّ بوضوح كذلك: لا يوجد توثيق ولا تخويل عبر مبدأ الأمان — فالمبدأ مجرد نص. والمرشِّح يفترض أن الهوية اشتُقّت بشكل صحيح أعلى المسار؛ والتوثيق يبقى من شأن الطبقة الأعلى.

وعلى الجانب العلائقي يؤدي أمان الصفوف في Postgres العمل نفسه. فإرشاد pgvector لدى Supabase (Supabase — RAG with Permissions) يطبّق السياسات على جدول document_sections، ويستمر البحث الدلالي في احترامها.

sql
alter table document_sections enable row level security;

create policy "read permitted sections"
on document_sections for select
to authenticated
using (
  document_id in (
    select id from documents
    where owner_id = auth.uid()
  )
);

وتصل الهوية عبر auth.uid() على REST، أو عبر متغيّر جلسة current_setting() على اتصال Postgres مباشر. وتضيف الوثائق تحفظاً محدداً: مع Foreign Data Wrapper، يكون أمان الصفوف حساساً لزمن الاستجابة، ويلزم تحليل خطة الاستعلام قبل التشغيل الفعلي.

واختيار الترشيح داخل المحرك بدل التقليم داخل التطبيق ليس مسألة سرعة وحدها. فمبرر Azure المعلن أن الترشيح داخل المحرك يزيل أيضاً كوداً مخصصاً لحلّ المجموعات المتداخلة واجتياز قوائم ACL متعددة المستويات. وما إن يُكتب ذلك الكود حتى يصير نسخة ثانية من نموذج صلاحيات المؤسسة، والنسختان تتباعدان.

نقل الهوية إلى الاستعلام

ومن أين تأتي قيمة المرشِّح لا يقلّ أهمية عن المرشِّح نفسه. فالثقة بقائمة مجموعات يرسلها العميل تعني أن المرشِّح لا يفرض شيئاً: إذ يتوقف التقليم على قيمة يختارها المتصل. والمسار الصحيح يشتقّ الهوية من رمز متحقَّق منه.

وعلى مسار ACL الأصلي في Azure AI Search (Azure AI Search — Document-level access control) يسافر رمز المستخدم في ترويسة x-ms-query-source-authorization. وتستخرج الخدمة ادعاءات المستخدم والمجموعة والنطاق من الرمز، وتقارنها ببيانات الصلاحية في الفهرس، ولا تعيد إلا المستندات المصرّح بها. وبشكل منفصل، يُفحص التطبيق المتصل بحثاً عن دور Search Index Data Reader — أي فحص من مرحلتين. وتقع هذه القدرات في إصدار REST قيد المعاينة؛ فأكّدوا نص الإصدار الدقيق من وثائق Microsoft Learn الحالية قبل كتابته في استدعاء. وتوثّق الخدمة أربعة مسارات رسمية: مرشحات الأمان (متاحة عامةً، ومستقلة عن الواجهة)، وقوائم ACL على نمط POSIX مع نطاقات RBAC، وتسميات الحساسية من Microsoft Purview، وقوائم ACL في SharePoint M365، والثلاثة الأخيرة قيد المعاينة.

وعلى Google Vertex AI Search (Google Cloud — Vertex AI Search: data source access control)، تُقدَّم قوائم ACL عند الإدخال عبر حقل acl_info في بيانات المستند الوصفية، مبنيّة على هيئة readers ← principals ← group_id أو user_id. وتأتي الهوية من Google Identity أو Workforce Identity Federation (Microsoft Entra ID، أو Okta، أو Ping)، ويجب أن تُطابِق سمة google.subject حقل البريد لدى المزوّد الخارجي. وحدّان يشكّلان التصميم مباشرة: 3000 قارئ لكل مستند، وكون إعداد ACL يُثبَّت عند إنشاء مخزن البيانات ولا يمكن تغييره بعدها.

ماذا يحدث حين تتغير الصلاحيات

هذا أكثر أجزاء المعمارية إفلاتاً من الانتباه. فحين يُزال مستخدم من مجموعة، يكون الأثر في النظام المصدر فورياً؛ وفي الفهرس ليس كذلك. وتنص وثائق Azure على ذلك صراحة: يقع تأخّر زمني قبل أن تتعرّف واجهة المعاينة على تغيّرات قيود الوصول أو الصلاحية تلك. وتغيّرات الصلاحية في النظام المصدر — عضوية مجموعات Entra، وقوائم ACL في ADLS Gen2، وإسناد تسميات Purview، وقوائم ACL في SharePoint — لا تنعكس في نتائج البحث إلا بعد مزامنة تلك البيانات الوصفية إلى الفهرس، عبر تشغيل مفهرس، أو تحديث بواجهة الدفع، أو إنعاش من Purview.

ومع SharePoint يكون التمييز أدقّ. فالتغيّرات على العناصر ذات الصلاحيات الفريدة تُلتقَط تدريجياً عند كل تشغيل ناجح للمفهرس، بينما تستدعي التغيّرات الموروثة من نطاق أب — موقع، أو مكتبة، أو قائمة، أو مجلد — إنعاشاً صريحاً: ‏/resync with options: ["permissions"]، أو ‏/resetdocs. ولذلك قد لا تنتشر صلاحية شُدِّدت على مستوى المكتبة من تلقاء نفسها، حتى مع تشغيل المفهرس وفق جدوله.

وتعالج Elasticsearch المسألة نفسها بمعمارية مختلفة (Elasticsearch — Document level security and connector access control sync). فهناك نوعان منفصلان من المزامنة: مزامنة المحتوى، إلى فهرس ببادئة ‎search-‎، ومزامنة التحكم في الوصول، إلى فهرس مخفي ببادئة ‎.search-acl-filter-<INDEX-NAME>‎. والحقل على مستندات المحتوى هو ‎_allow_access_control؛ ويُمنح الوصول حين يطابق مدخل واحد على الأقل في مستند التحكم في الوصول الخاص بالمستخدم مدخلاً في ذلك الحقل، والقيمة الفارغة تغلق المستند أمام الجميع.

json
{
  "title": "Q3 procurement note",
  "body": "...",
  "_allow_access_control": [
    "group:finance-readers",
    "group:procurement"
  ]
}

والطريقة الموثّقة لمعالجة تغيّرات الصلاحية هي وضع انتهاء صلاحية على مفتاح واجهة Elasticsearch وجدولة مزامنات وصول متكررة؛ فإن تغيّرت صلاحيات مستخدم، لزم تحديث المفتاح أو إعادة إنشائه. وفي سلوك DLS عموماً، حين يحمل مستخدم أدواراً متعددة على الفهرس نفسه تُدمج استعلامات الأدوار بـ OR — فدور واحد مطابق يجعل المستند ظاهراً. وDLS لا ينطبق على واجهات الكتابة، ولأنه يعمل مع كل استعلام فهو يحمل كلفة أداء صغيرة.

وقاعدة التصميم المترتبة على ذلك قصيرة: فترة الإنعاش معامل أمان. وزمن الانتشار من تغيّر الصلاحية إلى الفهرس ينتمي إلى عقد النظام المكتوب، ويُختار لكل مستوى تصنيف.

البيانات الوصفية التي تسقط أثناء التقطيع

واستراتيجية التقسيم في المخازن متعددة المستأجرين تنتمي إلى الصورة نفسها. فـ Qdrant توصي بمجموعة واحدة لكل نموذج تضمين مع تقسيم قائم على الحمولة (Qdrant — Multitenancy)؛ ومنذ الإصدار v1.11.0 يجمع معامل is_tenant: true على فهرس كلمات مفتاحية أشعة المستأجر في موضع واحد، وعند التوسّع يمنح payload_m في hnsw_config مقترناً بـ m عالمي قدره 0 فهرسةً مستقلة لكل مستأجر. وتلاحظ الوثائق نفسها أن الاستعلامات العامة بلا مرشِّح مجموعة تبطؤ لأنها مضطرة لمسح كل المجموعات. وتوصي Pinecone بمساحات الأسماء لعزل المستأجرين وتعرّف معاملات البيانات الوصفية ‎$eq و‎$ne و‎$gt و‎$gte و‎$lt و‎$lte و‎$in و‎$nin و‎$exists و‎$and و‎$or، ولا يُسمح إلا بـ ‎$and و‎$or في المستوى الأعلى من تعبير الترشيح. وتعزل Weaviate كل مستأجر في شظيته الخاصة بفهرس شعاعي خاص به. وتبني Amazon Bedrock Knowledge Bases التحكم في الوصول من مرشحات البيانات الوصفية وتسمح بما يصل إلى 10 كيلوبايت من البيانات الوصفية المخصصة لكل مستند.

وأياً كان المخزن المختار، ثمة خطوة إعداد تُغفَل كثيراً. فتلاحظ وثائق Azure أنه حين تقطّع مجموعة مهارات المستند — مهارة Text Split أو التوجيه الشعاعي المدمج — يجب حمل حقول بيانات الصلاحية من تعيينات حقول المفهرس إلى إسقاطات الفهرس. ومع تسميات Purview تكون الصياغة صريحة: بدون هذا الإسقاط، لا تُرشَّح المراجع على مستوى المقطع. فالتقطيع، إن أُسيء إعداده، يُسقط بيانات الصلاحية ويترك خلفه مقاطع بلا ترشيح. وهذا غير مرئي من الخارج: فالفهرس يُبنى، والبحث يعمل، وكل ما هناك أن المرشِّح لا يطابق شيئاً ذا معنى.

سجل التدقيق ومرحلة التوليد

والنصف الآخر من التحكم في الوصول هو سجل أي استعلام لمس أي مستند. وتُعدّد OWASP RAG Security Cheat Sheet ذلك: سجّلوا كل استرجاع بهوية الوكيل أو المستخدم صاحب الاستعلام وببيانات التحكم في الوصول الخاصة بالمقاطع المسترجعة، وغطّوا المسار كاملاً — الاستعلام المستلَم، والمقاطع المسترجعة بمعرّفات مستنداتها وبيانات وصولها، ومدخل النموذج المجمَّع، ومخرج النموذج المولَّد، وأي استدعاءات أدوات نُشِّطت. والجزء الذي يُتخطّى عادةً هو التخزين المؤقت: فإصابات المخزن المؤقت يجب أن تُسجَّل بالتفصيل نفسه الذي تُسجَّل به الاسترجاعات الطازجة، وإلا انقطع السجل. وكل إدراج وتحديث وحذف على الفهرس ينبغي تسجيله بطابع زمني وبهوية من قام به. ويشير الإجراء الرابع في LLM08:2025 إلى الاتجاه نفسه: احتفظوا بسجلات مفصّلة وغير قابلة للتعديل لأنشطة الاسترجاع.

والطبقة الأخيرة هي التوليد. فحتى مع استرجاع مرشَّح بشكل صحيح، يمكن لمسار متعدد الخطوات أن يسحب محتوى خارج النطاق إلى الجواب عبر ملخص، أو ملاحظة وسيطة، أو مخرج أداة. والإجراءات المعيارية لدى OWASP لهذه المرحلة: تحقّقوا من كل مخرجات النموذج قبل إعادتها، وطبّقوا مرشحات السياسة والحجب للبيانات الشخصية والأسرار، واحجبوا ديناميكياً بحسب مستوى وصول المستخدم صاحب الاستعلام، ووقّعوا بيانات إسناد المصدر كي يتعذّر العبث بها بعد التوليد. وعملياً يعني ذلك أن كل مقطع يستند إليه الجواب يحمل معرّفه معه، وأن الفحص الأخير يجري في طبقة مستقلة عن النموذج الذي أنتج الجواب.

لا توجد دراسة عامة مقيسة معروفة عن تسرّب المحتوى عبر التلخيص. فتعاملوا معه كفرضية تصميم لا كنتيجة تجريبية: فكل ما يدخل سياق النموذج يمكن أن يظهر في الجواب، ولذلك ينتمي الضبط إلى طبقة الاسترجاع.

التحكم في الوصول داخل RAG ليس اختصاصاً أمنياً جديداً. بل هو نموذج الصلاحيات القائم يُحمل إلى مسار بيانات جديد. والأمر كله يختزل إلى أربعة أسئلة. هل تعيش بيانات الصلاحية على مستوى المقطع؟ وهل يُطبَّق المرشِّح داخل المحرك، أثناء اجتياز البحث؟ وهل تُشتقّ الهوية من رمز متحقَّق منه بدل أن يقدّمها العميل؟ وكم دقيقة تمرّ بين تغيّر صلاحية في النظام المصدر وأثره في الفهرس؟ والنظام القادر على الإجابة عن هذه الأربعة كتابةً نظام قابل للتدقيق. أما العاجز عنها فلا جواب لديه عن سؤال ما الذي يعيده الفهرس كذلك.

المصادر

  • OWASP Gen AI Security Project — LLM08:2025 Vector and Embedding Weaknesses
  • OWASP — RAG Security Cheat Sheet
  • Azure AI Search — Filters in vector search (vectorFilterMode, prefilter/postfilter latency figures)
  • Azure AI Search — Security filters for trimming results
  • Azure AI Search — Document-level access control (ACLs, RBAC scopes, Purview labels, SharePoint indexer)
  • Supabase — RAG with Permissions (pgvector and row level security)
  • Google Cloud — Vertex AI Search: data source access control
  • Elasticsearch — Document level security and connector access control sync
  • Qdrant — Multitenancy
  • Pinecone — Namespaces and metadata filtering
  • Weaviate — Multi-tenancy
  • Amazon Bedrock Knowledge Bases — Metadata filtering

مقالات أخرى

لديكم نظام تبنونه، أو نظام قائم تطوّرونه؟

No deck needed. 20 minutes. The rest is up to you.