SaaS · عزل العملاء

سياق العميل في SaaS: مرّر الهوية دون تسريب البيانات

معمارية إنتاجية لاشتقاق هوية العميل الموثوقة وتمريرها عبر الواجهات والمهام وفرض العزل عند كل حد للبيانات.

المسار المرتبطالسحابة وDevOps وKubernetes
رسم تصوري لمعمارية أمان SaaS يوضح سياق عميل موثقًا يمر عبر مسارات API والخدمات والطوابير وقاعدة البيانات المعزولة مع حجب محاولة عبور بين العملاء.
تصوّر بصري للفكرة — يتبعه شرح ومخطط تنفيذي داخل المقال.

التمرير الآمن لسياق العميل في SaaS يعني اشتقاق العميل النشط من هوية وعضوية موثقتين—وليس من `tenant_id` يرسله العميل—ثم حمل سياق أدنى وثابت عبر واجهات API والخدمات والمهام، مع إعادة التفويض عند كل حد للموارد. تبرز أهميته لأن تسجيل الدخول الصحيح لا يثبت أن المستخدم يملك صلاحية الوصول إلى كائن تابع لعميل محدد. تحتاج فرق المنتج والمنصة إلى تفويض على مستوى الطلب وعزل صريح في الاستعلامات والطوابير والذاكرة المؤقتة والتخزين والمراقبة لمنع كشف البيانات بين العملاء.

يعتمد هذا الدليل على إرشادات AWS لمعمارية SaaS والتفويض، وملفي JWT في RFC 9068 وRFC 8725، وأمن واجهات API من OWASP، وسياسات الصفوف في PostgreSQL، ووثائق OpenTelemetry التي روجعت في 5 أكتوبر 2026. الأمثلة نمط معماري وليست ادعاءً بأن شكل توكن واحدًا أو محرك سياسات واحدًا يناسب كل نظام.

سياق العميل حقيقة أمنية مشتقة

قد يرسل المتصفح اسم مساحة العمل أو معرّف الحساب أو المتجر للتعبير عن وجهة التنقل. هذه القيمة ليست دليلًا. عند أول حد موثوق، تحقّق من توقيع توكن الوصول والخوارزمية المسموحة والمُصدر الدقيق والجمهور المقصود والانتهاء وبقية الادعاءات المطلوبة. بعدها اربط الزوج `(issuer, subject)` بعضوية نشطة لدى العميل المطلوب. يحذر RFC 8725 من أن الأنظمة التي تضم عدة مُصدرين أو مستلمين يجب أن تتحقق من المُصدر والجمهور لمنع استبدال التوكن، ويلزم RFC 9068 خادم المورد برفض توكن JWT إذا لم يعرّف الجمهور هذا المورد.

ينبغي أن تكون النتيجة سياقًا داخليًا ينشئه كود موثوق، لا مجرد إعادة تسمية لترويسة عامة. يمكن أن يضم السياق المفيد `tenantId` و`actorId` ونوع الفاعل والنطاقات أو مراجع الأدوار وقوة المصادقة وإصدار السياسة ومعرّف الطلب وحدث الهوية المستخدم للتدقيق. أدرج ما تحتاجه الخدمات اللاحقة فقط، وثبّت الكائن طوال العملية كي لا تستبدل middleware العميل بصمت في منتصف الطلب.

تصف AWS هوية SaaS بأنها وصل هوية المستخدم بهوية العميل، وتوضح أن هذا السياق ينبغي أن يعبر المعمارية. لكن التفسير الهندسي المهم هو أن التمرير ليس تفويضًا. إنه يوفّر مدخلات موثوقة لكل قرار سياسة، بينما تظل الخدمة المالكة للمورد مسؤولة عن تقرير ما إذا كان الفاعل يستطيع تنفيذ العملية داخل ذلك العميل.

لا تجعل ترويسة العميل العامة مصدر سلطة

ترويسة عامة مثل `X-Tenant-ID` مريحة للتوجيه لكنها غير آمنة كمصدر للحقيقة؛ يستطيع المهاجم تغييرها. إذا احتوى المسار `/tenants/:tenantId` فقارن العميل المطلوب بالعضوية المشتقة من الهوية الموثقة. وفي المنتجات التي ينتمي فيها المستخدم إلى عدة عملاء، يجب أن يكون تبديل العميل عملية صريحة تختار أو تصدر سياقًا مصرحًا به، لا معرّفًا حرًا ينتقل إلى عمق النظام.

قد يتحقق الـgateway من التوكن الخارجي مرة، لكن الخدمات اللاحقة يجب أن تستقبل إثباتًا داخليًا موقعًا، أو طلبًا موثقًا بهوية workload وسياق يملكه الخادم، أو سياقًا يعاد بناؤه من ادعاءات موثوقة. احذف أي نسخة أرسلها العميل من الترويسات الداخلية قبل إضافة القيمة الموثقة. ولا تمرّر توكن المستخدم الأصلي عشوائيًا إلى كل خدمة؛ فتوكنات الخدمات المقيدة بالجمهور أو هوية workload تقلل إعادة استخدام التوكن واتساع الصلاحيات. احتفظ بهوية الإنسان بصورة مستقلة للتدقيق والتفويض المفوض.

عامل حقول السياق كعقد بإصدار. يجب أن تفشل الخدمة بصورة مغلقة إذا غاب العميل أو كان مشوهًا أو غير مدعوم. افتراضات مثل `tenantId = public` تحول عطل التمرير إلى تسريب بيانات. ولتفحّص الصحة أو مسارات control plane الخالية فعلًا من العملاء، استخدم handlers مستقلة بدل أعلام تجاوز مخفية داخل middleware الطلبات العادية.

فوّض الكائن لا المسار فقط

تسمي OWASP غياب التحقق من حق المتصل في تنفيذ عملية على كائن بعينه Broken Object Level Authorization أو BOLA. كل endpoint يستقبل معرّف كائن مرشح لهذا الخلل. تقلل UUIDs العشوائية سهولة التخمين لكنها لا تمنح صلاحية. فضّل دوال repository تفرض تمرير العميل والكائن معًا، مثل `findInvoice(tenantId, invoiceId)`، حتى يصبح الاستعلام غير الآمن ذي الوسيط الواحد صعب الاستدعاء.

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

يمكن لنقطة فرض السياسة عند API أو حدود الخدمة أن تسأل نقطة قرار مركزية أو مدمجة عن الفاعل والعميل والفعل والمورد والسمات اللازمة. تميز إرشادات AWS بين التفويض متعدد العملاء وعزل العملاء: قد يكون المستخدم موثقًا ويبدو مصرحًا له بينما يقرأ النظام مورد عميل آخر. افرض الاثنين.

اشتق هوية العميل عند حد الثقة، ومرّر سياقًا ثابتًا وأدنى، ثم أعد فرضه عند كل حد للموارد.
اشتق هوية العميل عند حد الثقة، ومرّر سياقًا ثابتًا وأدنى، ثم أعد فرضه عند كل حد للموارد. اضغط لعرض أكبر

مرّر السياق بأمان في العمل غير المتزامن

تفصل الطوابير عمر المهمة عن الطلب الأصلي. على المنتج أن يضع في الرسالة مراجع العميل والفاعل الموثقين، وغرض العملية، وإصدار التفويض أو السياسة، وارتباط التتبع، ومفتاح idempotency مقيدًا بالعميل. لا تضع توكنات وصول أو refresh tokens أو cookies أو أي اعتماد قابل لإعادة الاستخدام داخل الحدث. يوثق المستهلك المنتج أو الطابور، ويتحقق من schema الغلاف، ويعيد بناء سياق يملكه الخادم، ثم يعيد فحص الحقائق المتغيرة مثل حالة العضوية وتعليق العميل والصلاحيات الحالية عندما تكون العملية حساسة.

لا تفترض أن التفويض لحظة enqueue يبقى صالحًا إلى الأبد؛ قد يزال المستخدم قبل تنفيذ تصدير طويل. اختر عمدًا بين التفويض وقت التنفيذ وبين capability معتمدة ذات فعل ومورد وانتهاء وإلغاء ضيقة، وسجّل القرار. أما الصيانة الخلفية التي تعمل كخدمة فتستخدم `actorType: service` وغرضًا مسمى وصلاحيات محدودة بدل انتحال مستخدم.

يدخل العميل أيضًا في إزالة التكرار والترتيب. صيغة مثل `idempotencyKey = tenantId + operation + externalId` تمنع رقم طلب تاجر متطابقًا لدى عميلين من إسقاط إحدى المهمتين. قد يحافظ التقسيم حسب العميل على ترتيب محلي، لكن امنع عميلًا كبيرًا من تجويع غيره عبر حدود تزامن وجدولة عادلة عند الحاجة. ضع الرسائل ذات سياق العميل المفقود أو المتناقض في quarantine بدل التخمين.

افرض العزل عند حد البيانات

فلاتر التطبيق ضرورية لكن نسيانها سهل. أضف طبقة فرض ثانية حين تدعم التقنية ذلك. تستطيع Row-Level Security في PostgreSQL تقييد الصفوف التي تقرؤها أو تعدلها الاستعلامات العادية. بعد تفعيل RLS يؤدي غياب سياسة مطابقة إلى الرفض الافتراضي. يتجاوز مالك الجدول والأدوار ذات `BYPASSRLS` السياسات عادة، لذلك يجب ألا يملك دور التطبيق جداول العملاء أو صلاحية التجاوز؛ واستخدم `FORCE ROW LEVEL SECURITY` عندما يتطلب نموذج الملكية ذلك.

مع connection pool اربط سياق العميل محليًا بالمعاملة كي يزول تلقائيًا عند نهايتها. إعداد الجلسة الدائم قد يتسرب إلى المستعير التالي. النمط التالي توضيحي؛ يجب أن تغطي السياسة الفعلية `USING` و`WITH CHECK` والأدوار المميزة والترحيلات وسلوك الفشل.

BEGIN;
SELECT set_config('app.tenant_id', $1, true); -- داخل المعاملة فقط

ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
ALTER TABLE invoices FORCE ROW LEVEL SECURITY;

CREATE POLICY tenant_invoices ON invoices
  USING (tenant_id = current_setting('app.tenant_id', true)::uuid)
  WITH CHECK (tenant_id = current_setting('app.tenant_id', true)::uuid);

SELECT * FROM invoices WHERE id = $2;
COMMIT;

يمتد الثابت نفسه خارج SQL. تحتاج مفاتيح الذاكرة المؤقتة بادئة العميل واختلافًا واعيًا بالتفويض. يجب أن تربط مسارات الملفات والروابط الموقعة ملكية العميل. ويجب أن تفرض استعلامات البحث والمتجهات فلتر العميل. كما تحتاج صادرات التحليلات قيد العميل قبل التجميع. اشتراك عدة عملاء في خدمة خلفية لا يلغي مسؤولية فريق التطبيق عن العزل.

صمّم غلاف سياق أدنى

افصل النقل عن السياسة. ترويسة HTTP أو metadata في RPC أو حقل حدث ليست إلا ناقلًا. تحولها الخدمة إلى كائن داخلي typed بعد توثيق مصدرها. قد يبدو الغلاف المدمج هكذا:

type TenantContext = Readonly<{
  tenantId: string;
  actor: { type: 'user' | 'service'; id: string };
  scopes: readonly string[];
  authn: { issuer: string; audience: string; strength?: string };
  policyVersion: string;
  requestId: string;
}>;

async function loadInvoice(ctx: TenantContext, invoiceId: string) {
  await authorize(ctx, 'invoice:read', { invoiceId });
  return invoices.findOne({ tenantId: ctx.tenantId, id: invoiceId });
}

تجنب نسخ JWT كامل أو ملف العميل إلى كل قفزة. قد تتغير خطة العميل والخصائص وحالة الفوترة؛ مرّر معرفات مستقرة وحمّل البيانات المتغيرة من مصدرها الرسمي أو cache محدودة. وقّع الإثباتات الداخلية عند عبورها مناطق ثقة، وقيد جمهورها وعمرها ودوّر المفاتيح. أصالة السياق وحداثته وسريته متطلبات مستقلة.

اجعل المراقبة مفيدة من دون إنشاء تسريب جديد

تساعد السجلات والتتبعات الواعية بالعميل في تشخيص مسار عميل واحد عبر الخدمات الموزعة، لكنها قد تنشئ أيضًا طبقة بيانات مشتركة جديدة. تحذر OpenTelemetry من أن context propagation يعبر حدود الخدمات، ومن وضع بيانات اعتماد أو مفاتيح API أو معلومات شخصية في baggage لأنها قد تسجل أو تصل إلى خدمة لاحقة غير موثوقة.

استخدم معرّف عميل مضبوطًا أو بديلًا تشغيليًا غير قابل للعكس، لا أسماء العملاء أو البريد أو الأسرار. قيد من يستطيع البحث في telemetry الموسومة بالعميل، وحدد مدة الاحتفاظ، ودقق وصول الدعم. قد تجعل تسميات العميل عالية cardinality المقاييس مكلفة؛ احتفظ بالتحليل لكل عميل في السجلات أو التتبعات عندما لا يحتمل backend المقاييس ذلك، مع إبقاء مقاييس SLO الإجمالية. ولا تمرر قرارات التفويض كـbaggage غير موثقة.

قد يتبع trace context ناقلات HTTP والرسائل، لكن على المستهلك غير المتزامن إنشاء span أو ربطها وفق معنى العملية. ارتباط telemetry ليس الآلية التي تمنح الوصول. وإذا نزعت baggage، يجب أن يصل سياق العميل الموثق إلى المستهلك عبر عقد المهمة نفسه.

اختبر الفشل العابر للعملاء قبل الإنتاج

تثبت الاختبارات الإيجابية أن العميل يستطيع استخدام المنتج، بينما يحتاج العزل اختبارات سلبية تتعمد عبور الحد. أنشئ عميلين بكائنات متشابهة المعرفات وحاول القراءة والكتابة والحذف والتصدير والبحث بينهما. عدّل معرف العميل في المسار والترويسة العامة. قدم توكنًا صحيحًا بجمهور خاطئ. أعد تشغيل مهمة بعد إزالة العضوية أو تعليق العميل. أعد استخدام مفتاح idempotency لدى عميل آخر. وأعد اتصال قاعدة البيانات إلى pool بعد exception ثم أثبت أن الطلب التالي لا يرث سياقه.

اختبر الذاكرة المؤقتة والتخزين والبحث والمراقبة كلًا على حدة. تحقق من RLS بدور التطبيق الفعلي لا مالك الجدول. أضف اختبارات توليدية تغيّر الفاعل والعميل والمورد والفعل. شغّل مجموعة خفيفة لعبور العملاء عند كل تغيير في التفويض أو الوصول للبيانات، ومجموعة خصمية أوسع قبل الإصدار. راقب محاولات العبور المرفوضة وفشل غياب السياق وعدم تطابق إصدار السياسة دون تسجيل payload حساسة.

يمكن أن يبدأ rollout آمن بوضع report-only فقط حيث يمكن ملاحظة الرفض دون كشف بيانات. أما عزل طبقة البيانات فلا ينبغي أن يكون report-only: قارن السياسة المقترحة على fixtures شبيهة بالإنتاج، ثم فعّلها مع إجراء break-glass صريح ومسارات مميزة مدققة.

متى لا يكفي النمط أو لا تحتاجه

قد لا يحتاج نشر أحادي العميل إلى سياق عميل داخل كل عملية domain، لكنه يظل بحاجة إلى هوية workload واضحة وتفويض الموارد. يقلل نموذج silo لكل عميل بعض مخاطر البيانات المشتركة، لكنه يحتاج توجيهًا وفوترة وتشغيلًا واعيًا بالعميل وحماية من إرسال الطلب إلى silo خاطئ. وفي المقابل، إضافة معرف العميل لكل ترويسة لا تصلح نظامًا يفتقد فحص الكائن أو فرض طبقة البيانات.

استخدم SQL أو استعلام الخدمة العادي بدل محرك سياسات موزع عندما تكون قاعدة التفويض محلية وثابتة وسهلة المراجعة. أدخل نقطة قرار سياسة عندما تحتاج خدمات كثيرة إلى قواعد موحدة وغنية بالسمات وتستطيع المؤسسة تشغيل إصدارات السياسة وتوفرها وتدقيقها. لا تجمع كل البيانات داخل محرك السياسة؛ مرّر مدخلات القرار اللازمة للطلب وأبق بيانات العميل الحساسة في مصدرها الرسمي.

الثابت الأساسي بسيط: لا يجوز لأي عملية اختيار عميلها من مدخل غير موثوق وحده، ولا يجوز لأي وصول إلى مورد إغفال نطاق العميل. يجب أن تثبت البنية هذا الثابت عبر الاستدعاءات المتزامنة والمهام المؤجلة وقواعد البيانات والذاكرة والملفات والبحث وأدوات الدعم.

قائمة تحقق للإنتاج

قبل الإطلاق، تأكد أن الحافة تتحقق من التوقيع والخوارزمية والمُصدر والجمهور والانتهاء؛ وأن العضوية تربط الفاعل بالعميل المختار؛ وأن ترويسات العميل العامة تزال؛ وأن السياق الداخلي أدنى وثابت؛ وأن كل استعلام كائن يضم نطاق العميل؛ وأن المستهلك الحساس يعيد التفويض وقت التنفيذ؛ وأن الأحداث لا تحمل اعتمادات؛ وأن idempotency مقيدة بالعميل؛ وأن سياسات PostgreSQL تفشل مغلقة بدور التطبيق؛ وأن مفاتيح الذاكرة والتخزين والبحث تربط ملكية العميل؛ وأن telemetry لا تحمل أسرارًا أو معلومات شخصية؛ وأن الاختبارات السلبية بين العملاء تعمل باستمرار.

للتفصيل التنفيذي، تابع عزل العملاء في PostgreSQL، ومعالجة Webhooks الموثوقة، وتصميم عقود API، ومراقبة الأنظمة الخلفية. تغطي هذه الأدلة حدود البيانات والتكامل والتشغيل المحيطة بعقد السياق.

المراجع الرسمية

تدعم هذه المراجع سلوك الأدوات المذكورة. الأمثلة وقرارات التصميم توضيحية، ويجب تكييفها مع متطلبات المشروع وإصداراته.

إعداد: Noor Yasser

من القرار إلى التنفيذ

تعمل على تحدٍ تقني مشابه؟

أساعد الفرق على تحويل القرار المعماري إلى نطاق واضح وتنفيذ يمكن تشغيله ومراجعته بثقة.

احجز لقاءً لمدة ٣٠ دقيقةالخدمة المرتبطةهندسة الأنظمة الخلفية وتكاملات APIمشروع من الأعمالRentoor