أصدرت InvoiceShelf تصحيحاً للثغرة CVE-2026-55610، وهي خلل فادح يسمح لأي مالك في شركة ما بالاستيلاء على حسابات المستخدمين في شركة أخرى. نتجت هذه الثغرة، التي تم تقييمها بـ 8.7 على مقياس CVSS، عن غياب التحقق من نطاق المستأجر (tenant-scope check) في كود Laravel الخاص بالتطبيق.
كيف كان يعمل هذا الخلل
InvoiceShelf هي أداة SaaS مبنية على Laravel تتيح للشركات إدارة المستخدمين والفواتير والإعدادات من لوحة تحكم واحدة. تقرأ المنصة ترويسة مخصصة (custom header) لتحديد المستأجر (tenant). عندما يطلب "مالك" (Owner) سجل مستخدم، يتحقق الكود فقط مما إذا كان "هل مقدم الطلب هو مالك لشركته الخاصة؟".
لا يتحقق الكود أبداً مما إذا كان المستخدم المستهدف ينتمي إلى نفس المستأجر. يقوم ربط النموذج بالمسار الضمني (implicit route-model binding) في Laravel بتحويل معرف المستخدم (user ID) إلى صف في جدول المستخدمين العام، وتقوم سياسة التفويض بالموافقة على الطلب بناءً على دور مقدم الطلب فقط.
يمكن للمهاجم بالتالي:
- تزويد أي معرف مستخدم رقمي (numeric user ID) في رابط الطلب (URL).
- استلام سجل المستخدم الكامل، بما في ذلك البريد الإلكتروني.
- إصدار تحديث يتجاوز البريد الإلكتروني وكلمة المرور الخاصة بالضحية، بل وحتى إعادة تعيين الحساب لشركة المهاجم كمسؤول فائق (super-admin).
من الناحية العملية، حوّل مالك خبيث أداة إدارة الشركات إلى سلاح شامل للاستيلاء على الحسابات. لم تكن هناك حاجة لأي صلاحيات تتجاوز رتبة "مالك".
من المتأثر؟
كان كل عميل لـ InvoiceShelf يستخدم إصداراً قبل 2.4.1 معرضاً للخطر. ولأن الخلل يكمن في مسار معالجة الطلبات الأساسي، يمكن لأي مستأجر أن يكون هدفاً لأي مالك مستأجر آخر، بغض النظر عن الحجم أو الوضع الأمني. يشمل التأثير فقدان السرية (عناوين البريد الإلكتروني) وفقدان السلامة (تغييرات غير مصرح بها في كلمات المرور، والترقية إلى مسؤول فائق).
التصحيح
أصدر المطورون الإصدار 2.4.1، حيث تمت إضافة تحقق صريح من المستأجر قبل أي عملية قراءة أو كتابة على سجل المستخدم. يقوم الإصلاح بتحديد نطاق الاستعلام (scopes the query) بمعرف الشركة النشطة، مما يجبر Laravel على إرجاع الصفوف التي تنتمي فقط إلى مستأجر مقدم الطلب.
ما يجب على المطورين تعلمه
- لا تعتمد أبداً على المفاتيح الأساسية العامة (global primary keys) في تطبيقات تعدد المستأجرين (multi-tenant applications).
- طبق مرشح المستأجر (tenant filter) على كل عملية بحث في قاعدة البيانات، وليس فقط في إجراءات الحذف أو الإنشاء.
- اجعل سياسات التفويض تتحقق من دور الفاعل و تبعية الكائن المستهدف للمستأجر.
- تعامل مع ميزات الإطار الضمنية مثل route-model binding كأدوات تسهيل يمكن أن تخفي ثغرات أمنية ما لم تضف تحديداً صريحاً للنطاق (explicit scoping).
نظرة مستقبلية
تظهر هذه الحادثة خطراً أوسع نطاقاً لأي خدمة SaaS تشترك في الجداول. يجب أن تراجع عمليات التدقيق الأمني جميع نقاط النهاية (endpoints) التي تقبل المعرفات وتتأكد من فرض تحديد نطاق المستأجر بشكل موحد.
الخلاصة: يمكن لعملية تحقق واحدة مفقودة من المستأجر أن تحول دور مستخدم ذو صلاحيات إلى باب خلفي شامل. تحديد النطاق الصحيح ليس أمراً اختيارياً؛ بل هو أساس عزل البيانات في أي نظام متعدد المستأجرين.
