نهاية NextJS؟... اكتشاف 13 ثغرة أمنية جديدة
BBetter Stack
Computing/SoftwareBusiness NewsInternet Technology
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الفيديو القادم.