واتسابدمشقالسبت – الخميس·10:00 ص – 7:00 م

الـ deserialization عند حدّ الـ framework

ثغرة RCE بدرجة 10.0، ثم 6 ترقيعات DoS في 8 أشهر. حين يكون العقد "اقبل كل ما يرمّزه الـ protocol"، فإغلاق ثغرة يترك البقية.

الأمننُشر في 3 دقائق قراءة

الشيفرة التي تحوّل الطلب إلى كائنات

الـ React Server Components تنقل البيانات بين الـ server والمتصفح عبر protocol اسمه Flight. ودوال الـ server — تلك التي تضع فوقها 'use server' — تصل كطلب يحوّله الـ framework مجدداً إلى وسائط دالة. في مكان ما من هذا المسار توجد شيفرة مهمتها الوحيدة أن تأخذ بيانات خاماً يتحكم بها المهاجم وتعيد بناء قيم JavaScript حيّة منها.

في تلك الشيفرة كانت الثغرة CVE-2025-55182: RCE دون authentication، بدرجة 10.0 على مقياس CVSS، أُعلنت في 3 كانون الأول 2025. أصابت الإصدارات 19.0.0 و19.1.0 و19.1.1 و19.2.0 من react-server-dom-webpack وreact-server-dom-parcel وreact-server-dom-turbopack، وعولجت في 19.1.2 و19.2.1. وظهر PoC علني خلال أيام.

ثم تكرر الأمر

الجزء اللافت ليس الـ RCE، بل الـ 8 أشهر التي تلتها:

  • CVE-2025-55183 — كشف الشيفرة المصدرية. عولجت في 19.0.2 و19.1.3 و19.2.2.
  • CVE-2025-55184DoS، في اليوم نفسه.
  • CVE-2025-67779 — إصلاح سابقتها كان "غير مكتمل". عولجت في 19.0.3 و19.1.4 و19.2.3.
  • CVE-2026-23864 — الإصلاحات، بالجمع، كانت "غير مكتملة". عولجت في 19.0.4 و19.1.5 و19.2.4.
  • ثم CVE-2026-23869، ثم CVE-2026-23870، ثم CVE-2026-44907 في تموز 2026، وعولجت في 19.0.8 و19.1.9 و19.2.8.

6 ثغرات DoS على صنف الـ endpoints نفسه، كل واحدة ترقّع حالة فاتت التي قبلها. هذا ليس إهمالاً، بل ما يحدث حين يكون سطح الهجوم "كل ما يستطيع الـ protocol التعبير عنه" لا "الحقول التي صرّحت بها".

لماذا هذا الصنف صعب

الـ validation المعتاد للمدخلات ينجح لأنك تعرف الشكل الذي تريده. أما الـ deserializer فلا يملك هذه الرفاهية: عقده أن يقبل كل ما يستطيع الـ protocol ترميزه، لأن العملاء الشرعيين يرسلون ذلك بالضبط. وكل خاصية في الـ protocol — التداخل، والمراجع، والـ chunks المؤجَّلة، والقيم المتدفقة — هي أيضاً attack vector. وإغلاق واحد يترك البقية.

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

هل أنت معرَّض؟

أنت لا تُنصِّب هذه الحزم بنفسك، بل يفعل ذلك الـ framework — ولهذا يغفل عنها الناس. افحص الـ lockfile لا ملف الـ dependencies:

bash
npm ls react-server-dom-webpack react-server-dom-parcel \
       react-server-dom-turbopack

إن لم يُرجِع شيئاً، فلا وجود لبيئة RSC في تطبيقك ولا يصلك شيء من هذا. وإن أرجع شيئاً، فالإصدار الذي يهمّ هو الـ resolved version فعلياً، مهما كان عميقاً تحت next أو react-router أو bundler plugin.

ما العمل فعلياً

عامل كل تصدير عليه 'use server' كـ endpoint عام بلا authentication. لأنه كذلك. الدالة قابلة للوصول من أي شخص يصل إلى موقعك، سواء استدعاها component في تطبيقك أم لا، وسواء كانت الصفحة التي تستدعيها خلف تسجيل دخول أم لا. الـ authorization يوضع داخل الدالة.

تحقّق من الوسائط في runtime. توقيع TypeScript ادّعاء في وقت الـ compile عن caller حَسَن السلوك. والـ caller هنا هو الشبكة.

قيّد حجم المدخل. حدود لحجم الـ body وtimeout للطلب تحوّلان معظم ثغرات الـ DoS من انقطاع إلى طلب مرفوض، بما فيها التي لم تُنشر بعد.

رقّع على release train لا على العناوين. 7 ثغرات في 8 أشهر تعني أن الاشتراك في المصدر مرة واحدة أفضل من ردّ الفعل 7 مرات.

الشكل العام

كلما عرض عليك framework أن يحوّل طلباً مباشرة إلى استدعاء دالة، فهو قد وضع deserializer على الحدّ وأعطاه اسماً ودوداً. جد هذا الشقّ في ما تشغّله، وطبّق عليه القواعد أعلاه — فالأرقام المذكورة هنا ستُصبح قديمة خلال سنة، أما الشقّ فلن يزول.