نهاية NextJS؟... اكتشاف 13 ثغرة أمنية جديدة

BBetter Stack
컴퓨터/소프트웨어경제 뉴스AI/미래기술

Transcript

00:00:00لقد حدث ذلك مجددًا. هذا هو الفيديو الثالث لي تقريبًا حول ثغرات مكونات الخادم (Server Components) هذا العام
00:00:05ولا أعتقد أنني قمت بتغطيتها جميعًا. هذه المرة هناك 13 ثغرة أمنية (CVEs)، نعم 13 واحدة، عبر React
00:00:11و Next.js، 6 منها عالية الخطورة وتشمل تعطيل الخدمة (DoS)، وتجاوز
00:00:15البرمجيات الوسيطة (Middleware)، وحقن النصوص (XSS) وغيرها. ربما كانت مكونات الخادم خطأً.
00:00:20إليك الإصدار الأمني لـ Next.js، كما تعلمون، مجرد إصلاح لبعض المشاكل العادية هنا
00:00:28التي واجهوها هذا الشهر، وفي الأسفل طبعًا الحل هو ترقية
00:00:32جميع إصدارات Next.js الخاصة بك، وهذه هي الإصدارات المتأثرة. وتجدر الإشارة
00:00:36إلى أن TanStack لم يتأثر بهذا، وقد أكون منحازًا ولكن هذا سبب آخر لاستخدام
00:00:41TanStack بالنسبة لي. الآن لن أتطرق إليها جميعًا لأننا سنستغرق وقتًا طويلاً
00:00:44كما أنني لم أجد استغلالات (Exploits) فعالة لكل هذه الثغرات، لكنني أريد أن أريكم واحدة
00:00:48من كل فئة، وسنبدأ بتجاوز البرمجيات الوسيطة والوكيل (Middleware and Proxy bypass)
00:00:52والتي تمكنت من إعادة إنتاجها هي الخاصة بـ Pages Router. لدينا تجاوز للبرمجيات الوسيطة
00:00:56في Pages Router إذا كنت تستخدم i18n، وكما تلاحظون هذه الثغرة بتقييم خطورة 7.5 من 10.
00:01:02هذا مثال على تطبيق معرض للثغرة، في إعدادات Next.js قمت بتفعيل
00:01:06i18n وأعددت لغتين هما الإنجليزية والفرنسية، ولدي أيضًا ملف للبرمجيات الوسيطة
00:01:12الذي تم تغيير اسمه إلى proxy في الإصدارات اللاحقة لـ Next.js لمحاولة تجنب
00:01:16الارتباك الذي سأظهره لكم، ولكن أساسًا ما يجب أن تفعله البرمجيات الوسيطة هو السماح لنا
00:01:19بتعديل الطلب الوارد، سواء عن طريق إعادة توجيهه، أو إعادة كتابته، أو إضافة
00:01:24بعض الترويسات (Headers) أو ما شابه. في حالتي، أستخدمها بحيث إذا حاول أحد زيارة
00:01:28صفحة /secret، فإنها تتحقق مما إذا كان لديهم ملف تعريف ارتباط للجلسة (Session Cookie) أي أنهم مسجلون للدخول
00:01:32وإذا لم يكن كذلك، فيجب إعادة توجيههم لصفحة تسجيل الدخول، لذا نأمل أن يتمكن المستخدمون المخولون فقط
00:01:37من رؤية صفحتي السرية. في الأسفل لدينا أيضًا مطابقة (Matcher) بحيث أن أي برمجية وسيطة
00:01:41نطبقها على تلك الصفحة السرية تطابق أيضًا متغيرات اللغة الخاصة بها لأنه تقنيًا
00:01:45الآن بما أن لدينا لغتين، فلدينا ثلاث نسخ من هذا الرابط. في الصفحة السرية نفسها
00:01:50لدي بعض خصائص الخادم (Server-side Props) والتي يجب جلبها من الخادم وقت العرض
00:01:54ومرة أخرى بما أننا أعددنا تلك البرمجية الوسيطة، نظريًا فقط المستخدم المسجل يمكنه رؤية
00:01:58هذه القيم التي أستخدمها لاحقًا في الصفحة نفسها مثل البريد الإلكتروني، وعلم (Flag) و
00:02:03أيضًا عنوان رئيسي. مرة أخرى، المستخدم المخول فقط يجب أن يرى هذه القيم. لنضع ذلك
00:02:07تحت الاختبار، وأول شيء سأحاول فعله هو الوصول لتلك الصفحة السرية وكما ترون
00:02:11تمت إعادة توجيهي لتسجيل الدخول لأنني لم أسجل دخولي، مما يعني أن البرمجية الوسيطة تعمل
00:02:16ولكن ماذا لو تحولنا إلى مخترقين محترفين؟ حسنًا، يمكننا فعل ذلك أولاً بفحص العنصر
00:02:20أشياء القرصنة المجنونة تمامًا، ثم في سكربت Next Data هنا نحتاج للبحث
00:02:24عن معرف البناء (Build ID) الخاص بنا، في حالتي هو هذا هنا ويمكننا نسخه
00:02:28ثم نحتاج لكتابة رابط يبدأ بـ _next/data/ ثم معرف البناء الذي
00:02:32نسخناه للتو ثم اسم الصفحة التي نحاول الوصول إليها مع .json. بمجرد فعل ذلك
00:02:37كما تلاحظون نحصل على تلك الخصائص التي كان يجب أن تكون خلف البرمجية الوسيطة ومحمية
00:02:40والتي كانت في حالتي العلم، البريد الإلكتروني، العنوان، وأيضًا شيء يطلب منك الاشتراك
00:02:44لمزيد من أخبار المطورين والدروس والنصائح. لذا تفضل وافعل ذلك، آمل أن أكون قد أبهرتكم
00:02:48بمهارات القرصنة المجنونة هذه، ولكن لماذا يحدث هذا فعليًا؟ حسنًا، هذا الأمر
00:02:52سهل التفسير بقدر سهولة تنفيذه، فبما أن لدينا صفحة سرية وبعض خصائص الخادم
00:02:56في Next.js يتم تقديم خصائص الخادم من رابط يبدو هكذا، لكن البرمجية الوسيطة
00:03:00كان ينبغي أن تحمي هذا المسار. المشكلة هي أنه نظرًا لاستخدامنا لـ i18n، كان لدينا
00:03:05رابطان آخران وهما المتغير الإنجليزي والفرنسي، ويمكنك رؤية أن خصائص
00:03:09الخادم تحصل أيضًا على المتغيرين الإنجليزي والفرنسي، وكان لدى Next.js بعض الأخطاء
00:03:13في الكود مما يعني أنه إذا كان i18n مفعلًا، فإنه لم يكن يحمي الحالة الأساسية، لذا هذا
00:03:18لم يتم تضمينه في المطابقة لكن الآخرين نعم، النسخ الإنجليزية والفرنسية كانت
00:03:22محمية ولكن ليس الحالة الأساسية لـ /secret. يمكننا رؤية ذلك سريعًا إذا قمت بتغيير
00:03:26هذا الرابط للنسخة الإنجليزية، فسيتم إعادة توجيهي لصفحة تسجيل الدخول. ثغرة بسيطة للغاية
00:03:31إذن، لكن لأكون صادقًا معكم، تجاوزات البرمجيات الوسيطة تبدو دائمًا أسوأ مما
00:03:35هي عليه؛ ليست جيدة ولكن لا يجب عليك حماية الكثير بالبرمجيات الوسيطة وحدها على أي حال
00:03:40و Next.js لا ينصحون حتى بفعل ذلك. إذا كان لديك بعض البيانات الحساسة
00:03:44في تلك الخصائص ولم يكن لديك منطق توثيق في الخادم، حسنًا، أشعر أن جزءًا
00:03:48من المشكلة يقع عليك أيضًا، لذا دعونا ننتقل لواحدة أكثر ضررًا وهي تعطيل
00:03:53الخدمة (DoS)، كان هناك ثلاثة منها ولكن واحدة فقط تمكنت من إعادة إنتاجها بشكل موثوق
00:03:56وهي هذه هنا، وهي تعطيل الخدمة مع مكونات الخادم وهذا يؤثر على Next.js
00:04:01وكذلك أي شيء يستخدم حزمة React Server DOM والتي هي تقريبًا فقط Next.js
00:04:05وإطارات العمل الأخرى التي نسختها مثل Vinxi وبعض التفرعات الأخرى. TanStack
00:04:10Start لا يستخدم هذا لذا فهو غير معرض لها. وكما ترون هذه الثغرة أيضًا بتقييم
00:04:14خطورة 7.5 من 10. مع هذه، كل ما تحتاجه هو تطبيق Next.js بسيط جدًا وفيه
00:04:18تحتاج لاستخدام إجراء خادم (Server Action) ولكن مجددًا يمكن أن يكون بسيطًا جدًا. هذا هو
00:04:22الموقع قيد التشغيل وكما ترون عندما أقوم بتحديث الصفحة فلا يوجد تقريبًا
00:04:25أي تحميل، إنه فوري تقريبًا ولأضع بعض الأرقام خلف ذلك، إذا أرسلت هذا الطلب
00:04:29سترون أنه ينتهي في 0.02 ثانية، لكن إذا قمت الآن بتشغيل استغلالي ثم
00:04:34أرسلت هذا الطلب مجددًا، فقد استغرق هذه المرة ست ثوانٍ وكان ذلك من تشغيل الاستغلال
00:04:39مرة واحدة، فتخيلوا ماذا سيحدث إذا قمت بتكراره. الآن لفهم هذا الاستغلال نحتاج
00:04:42لمعرفة القليل عن بروتوكول React Flight وهو التنسيق الذي يستخدمه React
00:04:46لتسلسل (Serialize) أشجار المكونات والبيانات بين الخادم والعميل. ربما
00:04:50رأيتم هذا من قبل؛ في هذه الصفحة كان لدينا نموذج يحتوي على إجراء خادم. إذا ذهبت
00:04:54إلى علامة تبويب الشبكة (Network) هنا وضغطت على إرسال، سترون أن الحمولة (Payload) تُرسل
00:04:58كبيانات تبدو هكذا، وهي تبدو ككلام غير مفهوم، والشيء نفسه بالنسبة
00:05:02للاستجابة هنا. إذا قمنا بنسخ هذه الحمولة يمكنني شرح ما يحدث عندما
00:05:05تُرسل إلى الخادم. الخطوة الأولى هي إلغاء التسلسل (Deserialization) وستبدأ في الجزء 0 حيث لدينا
00:05:10هذا الرمز $k1. هذا $k1 هو في الواقع مجرد مؤشر يقول أنه سيكون هناك بعض بيانات النموذج هنا
00:05:16التي تبدأ بشرطة سفلية واحدة، لذا سيأخذ جميع المفاتيح الأخرى التي أرسلناها
00:05:20كجزء من تلك الحمولة وسيمر عليها جميعًا ويبحث عن سلسلة نصية تبدأ بـ
00:05:24شرطة سفلية واحدة وسيعرف أن هذا سيكون المفتاح وأن هذه هي القيمة. بمجرد
00:05:28فعل ذلك يمكنه القول أن لدينا الاسم، البريد، الرسالة وسيحول هذه البيانات ببساطة
00:05:32إلى هذا الكائن (Object) الموجود في الأسفل. لطيف وبسيط. لكن المشكلة في هذا النهج
00:05:36هي ماذا يحدث عندما نزيد الحجم. لنقل أنني أضفت مؤشرًا آخر هذه المرة يبحث عن $k2
00:05:41هذا سيبحث عن جميع المفاتيح التي تبدأ بشرطتين سفليتين. المشكلة
00:05:44الآن هي عندما نكون عند $k1 سيمر على جميع المفاتيح الستة هذه بحثًا عن تلك
00:05:48التي تبدأ بشرطة واحدة وعندما يذهب لـ $k2 سيفعل الشيء نفسه تمامًا
00:05:52ولكن بحثًا عن التي تبدأ بشرطتين. لذا نحن الآن نمر على 12
00:05:56مفتاحًا في المجمل. الآن هذا ليس سيئًا للغاية ولكن لنأخذ الأمر لأقصى حد. إذا أضفنا 199,999
00:06:03مفتاحًا عشوائيًا للحمولة التي نرسلها ثم غيرنا مصفوفتنا في الصفر هنا
00:06:07لتكون $k1 $k2 وصولاً إلى $k1000، فهذا يعني أنه سيضطر للبحث عن شرطة واحدة
00:06:12شرطتين ثلاث شرطات وصولاً لألف شرطة سفلية عبر الـ 200,000
00:06:17مفتاح الموجودة في حمولتنا وهذا يعني في المجمل أنه سيقوم بـ 200 مليون مقارنة
00:06:21للسلاسل النصية. وكما تتخيلون، سيؤدي ذلك لتعطيل خيط المعالجة (Thread) لعدة ثوانٍ.
00:06:25هذا هو التعديل (Commit) الذي أعتقد أنه أصلح المشكلة التي كنا نواجهها. ترون أن
00:06:28هناك الكثير مما يحدث في هذا التعديل وللأمانة هو معقد قليلاً ولكن سأحاول
00:06:33شرحه بأفضل ما يمكن. أساسًا هم يستخدمون الآن نظام المؤشر (Cursor) للمفاتيح
00:06:36لذا يقومون بتحميل كل الـ 200,000 مفتاح التي أرسلناها في حمولتنا في قائمة ثم
00:06:41نبدأ عند الصفر هنا حيث يبحث عن مرجع $k1 ويبدأ بالنزول
00:06:45في تلك القائمة بمؤشر لا يمكنه الرجوع للخلف. لذا ينزل هنا لـ $j1 ويرى أنه لا
00:06:50يطابق الشرطة الواحدة التي نحتاجها فينتقل لـ $j2 وهذا لا يطابق الشرطة الواحدة
00:06:54أيضًا فيستمر وصولاً لآخر قائمة المفاتيح عند $j199,999. وبمجرد وصوله
00:07:01لهنا يدرك أنه لا يوجد تطابق لـ $k1 فينتقل لـ $k2. الآن $k2 يبدأ بالبحث
00:07:06عن شرطتين سفليتين ولكن المشكلة أنه بما أن هذا نظام يعتمد على مؤشر وهذا المؤشر
00:07:09لا يمكنه الرجوع للخلف فهو ينتهي فورًا عند نهاية القائمة لذا سيكون ذلك أيضًا
00:07:14غير معرف ويستمر ذلك وصولاً لـ $k1000. لذا هذه المرة قمنا فقط
00:07:18بالمرور على 200,000 مفتاح. أساسًا هذا الإصلاح قلل عدد العمليات من
00:07:23$k*n حيث $k هو عدد مراجع $k و $n هو عدد المفاتيح وصولاً
00:07:27إلى $n+k. لذا في حالتنا انتقلنا من 200,000,000 عملية إلى 201,000 فقط لأن
00:07:33عليه المرور على كل المفاتيح وأيضًا مراجع $k تلك. أعتقد أن
00:07:37هذه التغريدة من Prime تلخص الموقف الذي نحن فيه. صنع بروتوكول خاص بك مع التسلسل
00:07:41أمر صعب للغاية لذا ليس من المستغرب أن نرى هذا الكم من المشاكل. في رأيي
00:07:46هم بحاجة لجعل Claude Mythos يقوم بمراجعة شاملة لكود React و Next.js. التالي
00:07:50لدينا أعلى ثغرة خطورة في المجموعة وهي تزوير الطلبات من جانب الخادم (SSRF) في
00:07:54تطبيقات Next.js. ترون أن هذه مصنفة 8.6 من 10 ولكن تجدر الإشارة
00:07:59أيضًا إلى أنها لم تؤثر على الاستضافات على Vercel؛ فقط الاستضافات الذاتية أو المزودين الآخرين.
00:08:04هذا الاستغلال بسيط جدًا للاستفادة منه. أولاً نحتاج لتشغيل خادم Next.js الخاص بنا
00:08:09ومجددًا يمكن أن يكون تطبيق Next.js افتراضيًا. لا تحتاج لإجراء أي تعديلات.
00:08:14بعد ذلك سنحتاج أيضًا لخادم داخلي. لنقل أن هذا الخادم لا يمكن الوصول إليه إلا من قبل
00:08:18خادم Next.js وليس من العالم الخارجي. لنقل أن هذا كان في بيئة سحابية.
00:08:23ثم ما سنفعله هو ببساطة إرسال طلب curl بسيط حيث نرسل
00:08:26طلب curl إلى تطبيق Next.js الخاص بنا. كان ذلك على المنفذ 3002 ونقول أننا نريد
00:08:31هدف الطلب (Request Target) أن يكون الخادم الذي نريد الوصول إليه على رابط localhost. إذا ضغطت
00:08:36الإدخال (Enter) الآن سترون ما سيعود. إنها في الواقع صفحة HTML لخادم Python
00:08:40حيث تعرض قائمة الملفات الأساسية وإذا عدت لخادم Python نفسه
00:08:45سترون أنه استقبل طلبًا واردًا جاء فعليًا من تطبيق Next.js.
00:08:49ولتصور ما فعلناه بشكل أفضل، لنقل أن الخط المنقط هنا هو استضافة Next.js
00:08:53فلدينا خادم Next.js وأيضًا بعض الخدمات الداخلية سواء كانت Redis
00:08:57أو قاعدة بيانات أو أي خدمة أخرى، وهذه الخدمة لا تريد أن يراها الجمهور
00:09:02لذا لا يمكن لأحد إرسال طلب curl إليها؛ سيفشل ببساطة لأنها محمية بجدار ناري
00:09:06ومغلقة ولا يمكن الوصول إليها إلا عبر Next.js. ما فعلناه هو أننا أرسلنا
00:09:10طلب curl لخادم Next.js وقلنا له: “هل يمكنك إرسال طلب بالنيابة عنا
00:09:15للخدمة الداخلية؟” وحصلنا على تلك المعلومات، فقمنا بتجاوز ذلك الجدار الناري
00:09:19بالمرور عبر Next.js الذي يملك صلاحية الوصول للخدمة الداخلية. السبب الجذري
00:09:23لهذا بسيط أيضًا، ففي طلب curl نرسل ترويسة upgrade websocket
00:09:28وعندما نرسل هذه الترويسات، ما يحدث في Next.js هو أننا نصل لهذا الجزء
00:09:32من الكود وهذا يقوم بحل المسارات في رابطنا، ولكن الرابط المحلل الذي نحصل عليه هنا
00:09:37هو في الواقع هدف الطلب الذي أرسلناه في طلب curl، لذا سيكون هذا هو
00:09:40الهدف الذي نحاول الوصول إليه في الخادم الداخلي وليس تطبيق Next.js فعليًا.
00:09:45ما يحدث مع هذا الرابط المحلل هو مروره بفحص واحد بالأسفل ليرى: هل
00:09:49للرابط المحلل بروتوكول؟ وفي هذه الحالة الإجابة نعم لأننا نستخدم HTTP وهو بروتوكول
00:09:55لذا يمضي قدمًا ويقوم بعمل وكيل (Proxy) لهذا الطلب لنا. الإصلاح لهذا يضيف ببساطة
00:09:58حارسين جديدين لدالة resolve routes التي كانت لدينا؛ نحصل الآن على
00:10:02قيمة منطقية finished وكذلك رمز حالة (Status Code). ما تفعله هذه الدالة فعليًا
00:10:06هو أخذ رابطنا ومعالجة ما إذا كان طلب وكيل مشروعًا بناءً على إعدادات
00:10:11إعادة الكتابة (Rewrites) والبرمجيات الوسيطة في Next.js، وإذا لم يكن كذلك فهذا يعني أن finished ستكون
00:10:15خاطئة (False)، لذا بالأسفل لدينا فحص: إذا كانت finished صحيحة ننتقل للخطوة التالية، وإذا
00:10:20كانت خاطئة فلن يعمل هذا، مما يعني أننا لن ننفذ طلب الوكيل
00:10:24وفي حالة طلب curl الذي أجريناه سابقًا، هذا هو بالضبط ما سيحدث
00:10:27ستكون finished خاطئة. الآن لو بطريقة ما كانت finished صحيحة، فالفحص
00:10:32التالي هو رمز الحالة؛ عندما تعمل resolve routes نحصل فعليًا على رمز حالة إذا كان
00:10:36الطلب HTTP، فسيكون 200 أو 404 أو أي شيء مشابه، لذا إذا كان هناك رمز
00:10:41حالة فهذا يعني أنه ليس طلب وكيل WebSocket صحيحًا لذا سيتم ببساطة تجاهل
00:10:45هذا السطر وعدم تشغيله. لقد حاولت حقًا التعمق في هذه المشاكل في هذا الفيديو
00:10:49لذا أخبروني إذا كنتم لا تزالون تشاهدون بالتعليق بشيء عشوائي، لا أعرف، مثل
00:10:53bar أو شيء من هذا القبيل، واشتركوا أيضًا إذا كنتم تقدرون المحتوى. بقي لدينا
00:10:58فئتان لنمر عليهما، لكنهما سيكونان أسرع قليلاً، وسأبدأ بـ
00:11:01تسميم الذاكرة المؤقتة (Cache Poisoning) حيث تمكنت من إعادة إنتاج هذه المشكلة المتوسطة هنا
00:11:04وهي تسميم الذاكرة المؤقتة في مكون خادم React، وهي مصنفة 5.4 من 10. لإعادة
00:11:09إنتاج هذه، لدي تطبيق Next.js ولكن لدي أيضًا شبكة توزيع محتوى (CDN) وهمية لمحاكاة
00:11:14بيئة حقيقية مستضافة. هذا يعني أنه إذا زرت موقعي لأول مرة عبر
00:11:18رابط الـ CDN ثم ضغطت على “تصفح المنتجات” وعدت وضغطت مجددًا، في المرة الأولى
00:11:23يجب أن يكون هناك فقدان لذاكرة التخزين المؤقت (Cache Miss) وفي المرة التالية إصابة (Cache Hit). يمكننا رؤية ذلك
00:11:27في السجلات هنا؛ في البداية كان لدينا miss لـ /products مع نص استعلام ثم في المرات
00:11:31التالية كان hit. ما فعلته بعد ذلك هو مسح الذاكرة المؤقتة لمحاكاة انتهاء صلاحيتها
00:11:35أو شيء آخر، والآن يمكنني إرسال طلب curl هذا. مع عودة ذلك لتطبيقي
00:11:39إذا ضغطت على تصفح المنتجات الآن، سترون أننا نحصل على الكثير من الكلام غير المفهوم، لذا نجحنا
00:11:44في تنفيذ هجوم تسميم الذاكرة المؤقتة. ما أعتقد أنه يحدث هنا هو عندما نرسل
00:11:47طلب curl مع ترويسة react server components بقيمة 1، فإن هذا الرابط سيعيد
00:11:52بيانات مكون الخادم بدلاً من HTML. ثم عندما يحتاج هذا للتخزين المؤقت، سيمر Next
00:11:58عبر دالة تتحقق مما إذا كانت بيانات مكون خادم أم لا. إذا كانت كذلك
00:12:02سيخزنها في الذاكرة المؤقتة كذلك، وإذا لم تكن كذلك سيخزنها كـ HTML. هذا يعني
00:12:07نظريًا أنه عندما يحاول المستخدم الحصول على نسخة الـ HTML من الصفحة، أي بمجرد
00:12:11الضغط على الزر، فلا ينبغي أبدًا أن تعيد بيانات مكون الخادم. المشكلة في
00:12:16حالتنا هي أنه بما أننا نملك نص استعلام في نهاية طلب مكون الخادم عبر curl
00:12:20فإنه لم يستوفِ المتطلبات للتحقق مما إذا كان مكون خادم أم لا، لأن
00:12:24هذا التحقق ببساطة يرى ما إذا كان ينتهي بـ .rsc لكن نسختنا تنتهي بنص الاستعلام، لذا
00:12:29يعتقد الآن أنها HTML فيخزنها في الذاكرة المؤقتة كـ HTML، لذا في المرة التالية التي يضغط فيها
00:12:33مستخدم على الزر، يحصل فعليًا على بيانات مكون الخادم لأنه اعتقد
00:12:37أنها HTML. الإصلاح لهذا كان بسيطًا للغاية، فعندما يجرون الفحص
00:12:41عما إذا كان ينتهي بـ .rsc فهم الآن ببساطة يتجاهلون نصوص الاستعلام. ننتقل الآن لـ
00:12:46الفئة الأخيرة من الثغرات، لدينا بعض ثغرات حقن النصوص (XSS)، وهذه هي التي تمكنت من إعادة إنتاجها
00:12:50وكما ترون تقييمها 6.1 من 10، وهي حقن نصوص في السكربتات ما قبل التفاعلية (before interactive)
00:12:55مع مدخلات غير موثوقة. أساسًا كل ما يعنيه ذلك هو أنه في Next.js إذا كان لدي وسم
00:12:59سكربت (script) لديه استراتيجية before interactive، وإذا كان لدي سمة (attribute) أخرى فيه
00:13:03تحتاج لنوع من المحتوى غير الموثوق، على سبيل المثال هذا يأتي من بارامترات البحث (search params)
00:13:08فيمكنني القيام بحقن نصوص؛ يمكنني فعل ذلك بجعل المستخدم يضغط على رابط مثل
00:13:12هذا مثلاً حيث قمت بتضمين الكثير من المحتوى في بارامتر البحث ذاك، إذا
00:13:16ضغط شخص ما على هذا الرابط فهذا ما سيراه؛ قد يظنون أن عليهم
00:13:19تسجيل الدخول للموقع مجددًا وعندما يضغطون على “تسجيل الدخول” ترون هنا أن هذا كان
00:13:22نموذج دخول مزيفًا تمامًا تم حقنه عبر بارامتر البحث، وهو يسمح للمهاجم أساسًا
00:13:26بتنفيذ أكواد JavaScript على جهاز الضحية، لذا أعتقد أن مثالاً أكثر واقعية
00:13:31سيكون سرقة ملفات تعريف ارتباط الجلسة في Chrome لتسجيل الدخول لكل ما تملك صلاحية وصول
00:13:34إليه. هذه الثغرة مثال بسيط على الهروب (Escaping) غير الصحيح ويمكننا رؤية ذلك بشكل أسهل مع
00:13:39هذه النسخة المبسطة؛ كل ما أفعله في بارامتر البحث هو أنني أولاً أغلق
00:13:43وسم السكربت ثم أفتح وسم سكربت جديدًا مع كل ما أريد تشغيله، وعندما نضغط
00:13:47الإدخال سترون أنني أحصل على ذلك التنبيه الذي يقول “pwned” كما أريتكم في تطبيقي.
00:13:51طريقة عمل هذه الثغرة هي أننا نحتاج لوسم سكربت في Next.js باستراتيجية before interactive
00:13:55ونحتاج أيضًا لبعض السمات في وسم السكربت تأخذ بياناتها من مصدر غير موثوق
00:13:59في حالتي يأتي ذلك من بارامترات الاستعلام. ما يفعله هذا في Next.js هو أن
00:14:04وسم السكربت هذا يتحول لشيء كهذا حيث لدينا dangerously set inner HTML
00:14:08الاسم نفسه يوحي بأنه سيكون خطيرًا، ولدينا أيضًا JSON.stringify.
00:14:12الشيء المهم معرفته عن JSON stringify هو أنه لا يهرب محارف HTML
00:14:17مثل أقواس الإغلاق، لذا ما يحدث فعليًا هنا هو أننا نأخذ كل وسوم السكربت
00:14:21التي أعددناها في Next.js، وهو يبحث عن المصدر (Source) الذي سيتم تعيينه هنا ثم
00:14:24بقية الخصائص ستحتوي على معرف تتبع البيانات وكذلك القيمة التي
00:14:29وضعناها في بارامتر الاستعلام، وهذا يوضع في JSON stringify. حسنًا، ما يتم
00:14:33عرضه فعليًا في الصفحة هو شيء كهذا؛ لدينا معرف تتبع البيانات الخاص بنا والذي
00:14:37كان بقية الخصائص ولكن لدينا أيضًا السلسلة النصية التي أدخلناها عبر بارامترات
00:14:41البحث، فإذا قمنا بتوسيع ما يبدو عليه هذا فعليًا في الصفحة، فسيظهر كالتالي:
00:14:45لدينا وسم السكربت، لدينا معرف تتبع البيانات، ولكن بعد ذلك لدينا فعليًا
00:14:49وسم إغلاق للسكربت، فينهي وسم السكربت الذي كان Next.js يحاول عمله وبعد
00:14:53هذا يمكننا تشغيل أي سكربت نريده في تلك الصفحة، ثم لدينا أيضًا هذا الجزء الإضافي
00:14:58في النهاية هنا لأنه لولا ذلك لتم عرض التحليلات (Analytics) في
00:15:01الصفحة كنص لأن HTML سيراها كنص، فهذا الجزء أساسًا يبتلع ذلك
00:15:05بحيث لا توجد علامات واضحة على حدوث شيء سيء في تلك الصفحة. وهكذا
00:15:09نكون قد غطينا 13 ثغرة لـ Next.js و React هذا الأسبوع وكيفية عمل بعضها. وللأمانة
00:15:14لا أعرف ماذا أعتقد بشأن هذا؛ أنا أكره هذا، وهذا يأتي من شخص كان قبل
00:15:18سنتين تقريبًا يقوم بكل مشاريعه بـ Next.js وكنت أظن أنه الأفضل والمستقبل
00:15:22ولكن يبدو أنه مجرد عقبة تلو الأخرى؛ يبدو أنهم تسرعوا في بضعة
00:15:26أشياء ثم اضطروا لإصلاحها لاحقًا. شخصيًا الآن، أنا مقتنع تمامًا بـ TanStack وأيضًا
00:15:31Astro عندما أحتاج لموقع يعتمد على المحتوى؛ يبدون أبسط بكثير بالنسبة لي. وللأمانة
00:15:35لقد أعجبني أيضًا ما تفعله Cloudflare مؤخرًا، لذا بدأت أنقل مشاريعِي
00:15:39ببطء إلى هناك، لكن لا يزال لدي حوالي 20 منها على Vercel وأحتاج للذهاب
00:15:43وتحديثها. ما رأيكم؟ هل ستكون مكونات الخادم مفيدة يومًا ما أم أننا جربناها وفشلنا؟
00:15:48أخبروني في التعليقات بالأسفل، واشتركوا، وكما هو الحال دائمًا، نراكم في
00:15:51الفيديو القادم.

Key Takeaway

تتطلب معالجة 13 ثغرة أمنية في Next.js، بما في ذلك ثغرة SSRF بخطورة 8.6، تحديثًا فوريًا للإصدارات الأمنية لتجنب تسريب البيانات الحساسة أو تعطيل خيوط المعالجة بالخادم.

Highlights

  • يواجه إطار العمل Next.js وReact ما مجموعه 13 ثغرة أمنية جديدة (CVEs)، من بينها 6 ثغرات مصنفة عالية الخطورة.

  • تسمح ثغرة تجاوز البرمجيات الوسيطة (Middleware) في Pages Router عند تفعيل i18n بالوصول إلى بيانات الخادم الحساسة عبر مسارات JSON المخصصة.

  • تؤدي ثغرة تعطيل الخدمة (DoS) في مكونات الخادم إلى زيادة وقت استجابة الطلب من 0.02 ثانية إلى 6 ثوانٍ عبر استغلال آلية تسلسل البيانات في بروتوكول React Flight.

  • تم تحديد ثغرة تزوير الطلبات من جانب الخادم (SSRF) بتقييم خطورة 8.6 من 10، وهي تؤثر بشكل أساسي على التطبيقات التي تعتمد على الاستضافة الذاتية.

  • يتسبب الاستخدام غير الآمن لسمات السكربت مع استراتيجية before interactive في ثغرات حقن النصوص (XSS) بسبب عدم هروب محارف HTML عند استخدام JSON.stringify.

  • يتطلب تأمين التطبيقات المتأثرة ترقية Next.js إلى أحدث الإصدارات الأمنية، حيث أن الأنظمة التي تعتمد على TanStack Start أو Astro لم تتأثر بهذه الثغرات المحددة.

Timeline

أزمة الثغرات الأمنية في مكونات الخادم

  • تتوزع 13 ثغرة أمنية جديدة عبر إطاري العمل React وNext.js مع تصنيف 6 منها كتهديدات عالية الخطورة.
  • تشمل التهديدات المكتشفة تجاوز البرمجيات الوسيطة وحقن النصوص وتعطيل الخدمة وتزوير الطلبات.
  • تعد ترقية جميع إصدارات Next.js المتأثرة إلى النسخ المصححة هي الطريقة الوحيدة لضمان سلامة التطبيقات.

تتكرر المشاكل الأمنية المرتبطة بمكونات الخادم (Server Components) للمرة الثالثة خلال عام واحد. تتركز المخاطر في النسخ التي تستخدم بروتوكولات تسلسل البيانات الخاصة بـ React. بينما تظل أدوات مثل TanStack بعيدة عن هذه المخاطر، تظل الحاجة ملحة لمراجعة شاملة للأكواد البرمجية لضمان عدم وجود ثغرات إضافية غير مكتشفة.

آلية تجاوز البرمجيات الوسيطة في Pages Router

  • تسمح ثغرة في إعدادات i18n بالوصول إلى بيانات الخادم السرية التي يفترض أن تحميها البرمجيات الوسيطة.
  • يتم استخراج معرف البناء (Build ID) من مصدر الصفحة للوصول إلى روابط البيانات بصيغة .json مباشرة.
  • يفشل النظام في حماية المسارات الأساسية عند تفعيل لغات متعددة، مما يترك البيانات عرضة للاختراق.

يحدث الخلل عندما لا يقوم Next.js بتضمين المسار الأساسي في مطابقة البرمجيات الوسيطة عند استخدام اللغات. يمكن للمهاجم بناء رابط يبدأ بـ _next/data/ متبوعًا بمعرف البناء واسم الصفحة للوصول إلى خصائص الخادم (Server-side Props) مثل البريد الإلكتروني أو عناوين الجلسات. يوضح هذا مخاطر الاعتماد الكلي على البرمجيات الوسيطة لحماية البيانات الحساسة دون وجود منطق توثيق إضافي في الخادم.

استغلال بروتوكول React Flight لتعطيل الخدمة

  • تعتمد ثغرة DoS على إرسال حمولة ضخمة تحتوي على آلاف المفاتيح العشوائية لإجهاد المعالج.
  • يؤدي نظام المقارنة التسلسلي القديم إلى تنفيذ ملايين عمليات مقارنة السلاسل النصية في الطلب الواحد.
  • يقلل الإصلاح الجديد عدد العمليات من نظام التناسب الهندسي إلى نظام خطي يعتمد على مؤشر ثابت.

يستخدم React بروتوكول Flight لتسلسل الأشجار والبيانات، حيث يتم البحث عن مؤشرات معينة مثل $k1 داخل الحمولة. عند إرسال 200,000 مفتاح عشوائي مع طلبات لمؤشرات متعددة، يضطر الخادم للقيام بحوالي 200 مليون مقارنة، مما يعطل معالجة الطلبات لعدة ثوانٍ. يعالج التحديث الأمني هذه المشكلة عبر تقديم نظام المؤشر (Cursor) الذي يمنع العودة للخلف أثناء فحص المفاتيح، مما يقلص العمليات إلى الحد الأدنى.

ثغرة SSRF وتجاوز الجدران النارية الداخلية

  • تسمح ثغرة تزوير الطلبات من جانب الخادم (SSRF) للمهاجمين باستخدام Next.js كوكيل للوصول لخدمات داخلية محمية.
  • يتم التلاعب بترويسة upgrade websocket لتوجيه الطلب نحو روابط localhost الداخلية.
  • يضيف التحديث حراسًا جددًا لدالة resolve routes للتأكد من مشروعية طلبات الوكيل قبل تنفيذها.

تكمن الخطورة في قدرة المهاجم على إرسال طلب curl إلى خادم Next.js يطلب منه جلب بيانات من خدمة داخلية مثل Redis أو قاعدة بيانات غير متاحة للعموم. بما أن الخادم يملك صلاحية الوصول لهذه الخدمات، فإنه يقوم بتمرير البيانات للمهاجم بالنيابة عنه. يعتمد الإصلاح على التحقق من حالة الانتهاء (finished) ورموز الاستجابة للتأكد من أن الطلب يطابق قواعد إعادة الكتابة المسموح بها فقط.

تسميم الذاكرة المؤقتة وحقن النصوص البرمجية

  • يحدث تسميم الذاكرة المؤقتة (Cache Poisoning) عند تخزين بيانات RSC كأنها ملفات HTML بسبب نصوص الاستعلام في الرابط.
  • تنشأ ثغرات XSS نتيجة عدم معالجة محارف HTML بشكل صحيح داخل JSON.stringify في وسوم السكربت.
  • يمكن للمهاجمين حقن نماذج دخول مزيفة أو سرقة ملفات تعريف الارتباط عبر روابط ملغمة ببارامترات البحث.

في ثغرة التسميم، يخدع المهاجم نظام CDN لتخزين بيانات مكونات الخادم المشوهة في مسار HTML العادي عبر إضافة نص استعلام يمنع التحقق من امتداد .rsc. أما في ثغرة XSS، فإن استراتيجية before interactive تسمح بإغلاق وسم السكربت الأصلي وفتح وسم جديد لتنفيذ أكواد خبيثة. يتطلب الإصلاح تجاهل نصوص الاستعلام عند فحص الامتدادات وضمان الهروب الصحيح لجميع المدخلات غير الموثوقة قبل عرضها في المتصفح.

Community Posts

View all posts