كان بإمكان محاسب بصلاحية "للقراءة فقط" إلغاء الفواتير ومسح سجل المدفوعات في أداة مسك الدفاتر مفتوحة المصدر Akaunting، مما يعرض الشركات الصغيرة لخطر فقدان البيانات الصامت. تم إصلاح هذا الخلل في الإصدار 3.2.0، لكن الخطأ — المتمثل في ربط فحوصات الأذونات بقائمة ثابتة (hard-coded) من أسماء الدوال — لا يزال يهدد أي نظام يعتمد على التحكم في الوصول القائم على الأدوار (role-based access control).

كيف تسلل هذا الخلل

تقوم واجهة برمجة التطبيقات (API) الخاصة بـ Akaunting بالتحقق من حقوق المستخدم من خلال الرجوع إلى قائمة مسموح بها (allowlist) لأسماء الدوال. غطت القائمة عمليات CRUD المعتادة — الإنشاء، القراءة، التحديث، والحذف — ولكنها أغفلت العديد من نقاط النهاية (endpoints) المسؤولة عن تغيير الحالة:

  • markSent
  • markCancelled
  • markReceived

نظرًا لأن تلك المعالجات (handlers) لم تكن مدرجة في القائمة، لم يقم إطار العمل (framework) باستدعاء روتين فحص الأذونات عند تشغيلها. كان بإمكان مستخدم لديه دور "للقراءة فقط" إرسال طلب GET بسيط إلى نقطة النهاية markCancelled وسيعامل النظام ذلك كتغيير مشروع للحالة.

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

ما أظهرته الاختبارات

ظهرت الثغرة الأمنية في صورة Docker الرسمية لـ Akaunting:

  • أدى طلب PUT قياسي لتحديث فاتورة إلى إرجاع 403 Forbidden، مما أكد أن مسار التحديث العادي محمي.
  • نجح طلب GET إلى نقطة نهاية الإلغاء دون حدوث خطأ في التفويض، مما كشف عن الفجوة.

لماذا يهم هذا الأمر

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

الإصلاح

يوسع الإصدار 3.2.0 خريطة الأذونات لتشمل إجراءات الحالة التي تم إغفالها سابقًا. ومنذ ذلك الإصدار فصاعدًا، يجب أن يمر أي طلب يغير حالة المستند — سواء تم وسمه كمرسل، أو ملغى، أو مستلم — عبر نفس عملية التحقق من الدور كما هو الحال في التحديث القياسي. وهذا يعيد التوقعات بأن دور "للقراءة فقط" لا يمكنه حقًا تعديل البيانات.

دروس للمطورين

  • لا تساوِ أبدًا بين أسماء الدوال والأمان. إضافة نقطة نهاية جديدة لا يعني توريث الحماية تلقائيًا؛ قم بمراجعة كل دالة عامة بحثًا عن أي آثار جانبية.
  • القوائم المسموح بها (Allowlists) تكون مكتملة بقدر اكتمال القائمة نفسها. القائمة الثابتة للأفعال "الجيدة" تترك بابًا مفتوحًا للإغفالات.
  • افصل بين الغرض وبين فعل HTTP. من المفترض أن يكون GET للقراءة فقط، ولكنه هنا قام بتغيير الحالة. قيد عمليات التغيير (mutations) لتقتصر على POST و PUT و DELETE و PATCH.
  • أتمت عملية فحص تغطية الأذونات. يمكن لأدوات التحليل الساكن (Static analysis) تحديد دوال المتحكم (controller methods) التي تفتقر إلى استدعاء التفويض، مما يساعد في اكتشاف الفجوات قبل إصدار البرنامج.
  • اختبر باستخدام حسابات ذات صلاحيات دنيا. استخدم الاختبار القائم على Docker مستخدمًا بصلاحية "للقراءة فقط"؛ إن تكرار مثل هذه السيناريوهات في خطوط أنابيب التكامل المستمر (CI pipelines) يظهر مشكلات مماثلة في وقت مبكر.

ما يجب مراقبته لاحقًا

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

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