السلام عليكم ،سعدتُ بالمشاركة مرة أخرى في المبادرة الصيفية لمجتمع إتقان، وأردت مشاركتكم مساهمتي الثالثة في مشروع رتق (RATQ)، والتي كانت عبارة عن سد فجوات في صلاحيات الحقول (Field-Level Access Control)، عبر حل الـ Issue التالية:
المشكلة والحل:
كانت المشكلة عبارة عن 3 ثغرات مختلفة في الظاهر لكنها تحمل نفس الخلل: النظام كان يمنح الصلاحية على مستوى الوثيقة ككل دون أن يقيّد الحقول الحساسة بداخلها. بمعنى آخر التحقق كان يسأل فقط "هل يملك هذا الشخص حق التعديل على هذه الوثيقة؟" دون أن يسأل: "وأي أجزاء بالتحديد يحق له لمسها؟"
في التعليقات (Comments.ts)، كان بإمكان كاتب التعليق عبر طلب PATCH عادي تغيير حقل resource ونقل تعليقه خلسة إلى مورد قرآني آخر مختلف تماما عما كُتب من أجله.
الحل كان قفل هذا الحقل مباشرة في الـ Schema ليصبح غير قابل للتغيير بعد الإنشاء تاركا حرية تعديل حقل content فقط.
في طلبات الوصول (AccessRequests.ts)، كان الناشر أثناء الموافقة أو الرفض قادرا على تعديل نص رسالة المتقدم (message) أو تغيير هويته (applicant) أو حتى المورد (resource) مما يكسر نزاهة سجل العمليات.
الحل كان قفل هذه الحقول الثلاثة بنفس طريقة القفل التصريحي تاركا للناشر صلاحية تعديل status وpublisher_notes فقط.
في البلاغات (Reports.ts)، لم يكن هناك أي شيء يمنع نفس المستخدم من إنشاء عشرات البلاغات المتطابقة على نفس المورد.
الحل كان إضافةbeforeValidate Hook يبحث عن بلاغ سابق بحالة "مفتوح" لنفس المستخدم ونفس المورد، ويرفض الإنشاء إن وُجد بمحاكاة تامة لنمط جاهز موجود مسبقا في AccessRequests.
الحل في هذه الحالات كان بإضافةaccess: { update: () => false }على تعريف الحقل نفسه بدلا من كتابة Hook يدوي يقارن القيمة القديمة بالجديدة قبل الحفظ. اخترتُ هذا القفل التصريحي لأن الخيار الثاني يبقى دائما هشا يكفي أن أنسى حالة واحدة لتنكسر الحماية بالطريقة الصحيحة يقوم النظام بتجاهل أي قيمة مرسلة لهذا الحقل تلقائيا قبل أن تصل لقاعدة البيانات حتى.
للتأكد من سلامة التعديلات أضفت اختبارات وحدة تغطي مصفوفة الصلاحيات كاملة لكل حقل مقفل (مطور، ناشر، أدمن، وزائر غير مسجل) إضافة لاختبارات التكرار في البلاغات ولم أكتفِ بذلك بل تحققت من كل سيناريو في الواجهة وأيضا عبر إرسال طلبات PATCH مباشرة من الـ Console في المتصفح لأن نجاح اختبار الوحدة وحده لا يكفي
فوائد وملاحظات من المساهمة:
امتلاكك لصلاحية تعديل وثيقة ما لا يعني امتلاكك صلاحية تغيير سياقها والفصل بين الاثنين مهم ضد التلاعب بالبيانات
لما يكون نمط أمني ناجح فالأولى نقله لبقية الأجزاء المتشابهة والالتزام بنمط ونطاق المشروع أفضل من اختراع حلول معقدة غير مطلوبة
التحقق من تفاصيل الـ Schema الفعلية قبل محاكاة أي كود: مثلا في طلبات الوصول كانت الحالة pending بينما في البلاغات open والتحقق من الـ Schema أولا منع خطأ صامت في الفحص
لما تكون تغييرات مختلفة من الأفضل تكون كل واحدة على حدة ثم اختبارها بعد كل خطوة وهذا ما قمت به في المشكلات الثلاث
نجاح الاختبارات الآلية لا يغني عن التحقق اليدوي المباشر وكل منهما يكشف زاوية مختلفة وحتى اختبار ال console مهم جدا
عند التعامل مع حقول العلاقات في الـ Hooks قد تصل البيانات كـ ID فقط أو ككائن يحمل تفاصيل المورد واستخراج رقم الـ ID أولا يضمن دقة البحث في قاعدة البيانات وتفادي الأخطاء الصامتة
وكل الشكر للأستاذ @أبو بكر عبد الرحمن على المراجعة ولكل المجتمع و المساهمين
بارك الله في جهود الجميع