ما الذي يصلحه إصدار Node.js في أكتوبر 2026
نشر Node.js في 21 أكتوبر 2026 تحديثات للفروع 22 و24 و26. ومن بين الثغرات العشر المُصلَحة، يصنّف المشروع CVE-2026-111497 بدرجة High: يكتب مفكك Brotli المدمج خارج حدود مخزن مؤقت داخلي عندما يتلقى بيانات مشوّهة. وعمليًا، كان تبديل كتلة غير صالح يُبلَّغ عنه كحاجة مؤقتة إلى مزيد من المدخلات بدل أن يكون خطأ نهائيًا في الصيغة، فيواصل المفكك العامل بالتدفق إضافة البايتات إلى مخزن داخلي من ثمانية بايتات. وتتراوح العواقب المحتملة بين انهيار العملية وكشف محتوى الذاكرة وتنفيذ تعليمات برمجية عن بُعد بصلاحيات عملية Node.
أما الخطورة فلها قراءتان. يقول Node.js إنها High؛ وتمنحها Google، بصفتها جهة ترقيم الثغرات، درجة CVSS v4 قدرها 9.2 بحسب تحليل Rapid7. وحتى 26 أكتوبر 2026، لا تعلم Rapid7 بأي استغلال فعلي، والثغرة غير مدرجة في كتالوج CISA للثغرات المستغَلة. فالثغرة إذن غير مستغَلة حتى الآن بحسب Rapid7: إنها فرصة للتحديث بهدوء، لا سببًا للانتظار.
الإصدارات المتأثرة والإصدارات المصلَحة
مرّر الجدول أفقيًا
| الحالة | الوضع | الإجراء |
|---|---|---|
| Node.js 22.x | متأثر قبل 22.23.4 | الانتقال إلى 22.23.4 |
| Node.js 24.x | متأثر قبل 24.22.1 | الانتقال إلى 24.22.1 |
| Node.js 26.x | متأثر قبل 26.12.1 | الانتقال إلى 26.12.1 |
| Node.js 20.x وسائر الفروع المنتهية الدعم | تُعامَل كمتأثرة، ولا إصلاح لها | الترحيل إلى 22.x أو 24.x |
| صورة Docker من نوع node:<الفرع> | متأثرة إلى أن تُعاد بناء الصورة | إعادة البناء بالخيارين --pull --no-cache |
| ملف Node تنفيذي مضمَّن في أداة (Electron، ملف تنفيذي مُجمَّع) | يتوقف على النسخة المضمَّنة | تحديث الأداة نفسها |
| برنامج آخر يضمّن مفكك Brotli نفسه | بحسب الجهة المطوّرة | قراءة نشرتها الخاصة |
هل أنت مكشوف: المعيار الحاسم
لا يكفي أن يكون Node المثبَّت ضعيفًا حتى يصبح قابلًا للاستهداف. تستهدف نشرة Node.js تطبيقًا يفكّ بـ node:zlib بيانات Brotli يقدّمها مهاجم، عبر zlib.createBrotliDecompress أو zlib.brotliDecompress. وتوضح Rapid7 أن مجرد وجود المفكك لا يثبت التعرّض: كل شيء يتوقف على ما يفكّه التطبيق. لكن لا تستنتج سريعًا غياب الخطر، فكثيرًا ما يمرّ Brotli عبر مكتبات لم تكتبها.
الربط برأس Content-Encoding: br قراءتنا نحن، وليس تأكيدًا من النشرة. ففي الشيفرة المصدرية العامة، يفكّ fetch المدمج في Node (undici) استجابات br بـ zlib.createBrotliDecompress، ويتضمن محوّل HTTP في axios فك الترميز نفسه، وتفعل شيفرة body-parser الحالية الأمر ذاته لأجسام الطلبات الواردة. فثمة تدفقان يجب فحصهما: ما يتلقاه تطبيقك من العملاء، وما يجلبه من خوادم الأطراف الثالثة. يمكنك التحقق من ذلك في شجرة اعتمادياتك بأوامر الجرد أدناه.
من أين يدخل Brotli غير الموثوق إلى تطبيق Node
- طلبات واردة مضغوطة — واجهة Express أو خادم مخصص يستخدم body-parser يفكّ جسمًا
Content-Encoding: brأرسله أي عميل. - استجابات خدمات الأطراف الثالثة —
fetchأو axios يستدعي واجهة برمجية أو موجز RSS أو webhook أو صفحة للمعاينة: الخادم البعيد هو من يختار ترميز الاستجابة. - العرض من جهة الخادم — تطبيق Next أو Nuxt يجلب بيانات بعيدة أثناء العرض يمرّ بهذه الاستدعاءات الصادرة نفسها.
- وسطاء وبوابات مكتوبة بـ Node — مكوّن يفكّ الضغط لفحص المحتوى أو تحويله أثناء مروره يفتح التدفقين معًا.
- معالجة الملفات — خدمة تقبل ملفات أو أرشيفات مضغوطة بـ Brotli وتفتحها بـ
zlib.brotliDecompress. - أدوات البناء والتكامل المستمر — سكربتات النشر والمهام التي تنزّل ملفات ناتجة: أقل تعرّضًا، لكنها تستخدم الملف التنفيذي نفسه وتُحدَّث في الوقت نفسه.
- لا مسار محدَّدًا — أولوية أدنى لا معدومة: يكفي اعتماد يُضاف لاحقًا لإنشاء التعرّض، كما يصلح الإصدار ثغرات أخرى.
جرد المجموعة في خمس عشرة دقيقة
قراءة النسخة على كل جهاز
node -vwhich -a node
كثيرًا ما تتعايش عدة ملفات تنفيذية: ملف النظام، وملف nvm، وملف داخل حاوية. يسردهاwhich -aجميعًا، وليس الأول دائمًا هو ما تستخدمه الخدمة.العثور على الملف التنفيذي الفعلي لكل عملية
ps -eo pid,user,args | grep -E '[n]ode|[p]m2'sudo readlink -f /proc/<pid>/exe
يعطي الأمر الثاني، بعد تمرير رقم العملية الذي وجدته، المسار الدقيق للملف الذي يشغّل العملية. وهو الجواب الموثوق الوحيد على جهاز يحمل عدة تثبيتات لـ Node.استجواب الحاويات
docker psdocker exec <conteneur> node -vdocker images | grep node
الحاوية العاملة لا تُصلح نفسها: تحتفظ بالصورة التي بدأت بها، حتى لو وُجدت صورة أحدث على الجهاز.البحث عن الاستدعاءات المباشرة في شيفرتك
grep -rEn "createBrotliDecompress|brotliDecompress" --include=*.js --include=*.mjs --include=*.ts . --exclude-dir=node_modules
كل سطر يظهر هو نقطة تفكّ فيها شيفرتك ضغط Brotli. انظر من أين تأتي البيانات: ملف أنتجته بنفسك لا يحمل الخطر ذاته الذي يحمله جسم طلب.البحث عن المكتبات التي تفكّ الضغط نيابة عنك
grep -rlE "createBrotliDecompress" node_modules --include=*.jsnpm ls body-parser axios undici
يسرد الأمر الأول الحزم المثبَّتة التي تستدعي المفكك؛ ويُظهر الثاني نسخ body-parser وaxios وundici الموجودة في المشروع. شغّلهما في كل مستودع، لا في الأحدث فقط.رصد الملفات التنفيذية المُجمَّعة
الأدوات التي تحمل Node خاصًا بها (تطبيقات Electron، الملفات التنفيذية المُجمَّعة) لا يغطيها
apt. ابحث عن نسختها في وثائقها وتحقق مما إذا أصدرت الجهة المطوّرة نسخة مصلَحة.تدوين النتيجة في جدول
سطر لكل عملية: الجهاز، العميل، فرع Node، النسخة، هل حُدِّد مسار Brotli أم لا. يحدد هذا الجدول ترتيب المعالجة، وسيكون دليلًا بعد إتمام التحديث.
بأي ترتيب تعالج المجموعة
مجموعة مواقع العملاء، بنسخها وتثبيتاتها المتباينة، لا تُحدَّث بحركة واحدة، ولا حاجة إلى ذلك. ابدأ بالخوادم المكشوفة على الإنترنت التي تتلقى أجسامًا مضغوطة أو تستدعي خدمات أطراف ثالثة: العرض من جهة الخادم، والبوابات، والوسطاء، والواجهات البرمجية العامة. ثم التطبيقات الداخلية والعمال، ثم أدوات البناء. أما التطبيقات على Node 20 فحالة منفصلة: لن يوجد لها إصلاح أبدًا، والمخرج الوحيد هو الترحيل إلى 22.x أو 24.x، وهو أمر يُخطَّط مع العميل بدل ارتجاله. تقدّم على دفعات من بضعة أجهزة، وتحقق من كل دفعة قبل الانتقال إلى التالية.
التحديث بحسب طريقة التثبيت
حزمة apt (مستودع NodeSource أو التوزيعة)
apt-cache policy nodejssudo apt-get updatesudo apt-get install --only-upgrade nodejsnode -v
اقرأ أولًا المصدر الذي يعرضهapt-cache policy: يجب أن يشير المستودع إلى الفرع الصحيح (node_22.x أو node_24.x). مستودع بقي على فرع منتهي الدعم لن يعرض الإصلاح أبدًا. وقد تحمل حزمة التوزيعة رقمًا مختلفًا: قارنه بنشرة توزيعتك.nvm
nvm install --reinstall-packages-from=current 22nvm alias default 22nvm use 22node -v
يثبّت الأمر أحدث نسخة 22.x متاحة؛ تحقق من أنnode -vيعرض 22.23.4 أو أحدث. افعل الشيء نفسه مع 24 إن كان هذا فرعك. الخدمات التي يشغّلها مستخدم آخر لها nvm خاص بها.Docker
غيّر الوسم في سطر
FROM، مثلFROM node:22أوFROM node:24مع النوع الذي تستخدمه أصلًا، ثم:docker build --pull --no-cache -t monapp:corrige .docker compose up -d
يجلب الخيار--pullأحدث صورة أساس، ويمنع--no-cacheإعادة استخدام طبقة بُنيت بـ Node القديم. لا تُستبدل الحاوية العاملة إلا عند إعادة النشر.pm2
pm2 restart allpm2 update
بعد استبدال الملف التنفيذي، يعيدpm2 restartتشغيل التطبيقات؛ ويحدّثpm2 updateخدمة pm2 الموجودة في الذاكرة. وتضيف وثائق pm2 أنه بعد ترقية Node ينبغي تحديث سكربت الإقلاع أيضًا:pm2 unstartuppm2 startuppm2 saveخدمة systemd أو مشرف آخر
sudo systemctl restart <service>
أعد تشغيل كل خدمة على حدة، وراجع سجلها ومسار الصحة فيها قبل الانتقال إلى التالية. وفي تطبيق خلف موزّع أحمال، أخرج الجهاز من المجموعة طوال مدة إعادة التشغيل.
العملية الجارية تحتفظ بـ Node القديم
استبدال الملف التنفيذي لا يصلح العمليات الجارية: فهي تحتفظ بالنسخة القديمة في الذاكرة حتى تُعاد. وإظهار node -v نسخة صحيحة على الجهاز لا يثبت شيئًا عن خدمة بدأت أمس. بعد كل إعادة تشغيل، تحقق من العملية نفسها: pm2 jlist أو pm2 show <nom> لمعرفة نسخة Node المستخدمة، أو اعرض process.version في مسار الصحة لديك.
إذا تعذّر التحديث اليوم
لم ينشر Node.js أي حل بديل: التخفيف الوحيد من الجهة المطوّرة هو النسخة المصلَحة. وعند تعذّر ذلك توصي Rapid7 بتقييد مسارات التطبيق التي تفكّ ضغط Brotli غير الموثوق أو تعطيلها. وهذا ما يعنيه عمليًا. النقاط التالية إجراءات لتقليل الخطر نقترحها نحن، وليست توصيات من الجهة المطوّرة، ولا يحلّ أي منها محل الإصلاح.
في الطلبات الواردة، ارفض Brotli عند الوسيط العكسي، مثلًا في nginx: if ($http_content_encoding ~* br) { return 415; }. اختبر بـ nginx -t قبل إعادة التحميل، وتحقق من أن أيًا من عملائك الشرعيين لا يرسل أجسامًا بترميز br. وفي استجابات الأطراف الثالثة، أعلن الترميزات المفيدة فقط عبر الرأس Accept-Encoding: gzip، وقيّد الوجهات المسموح بها لاستدعاءاتك الصادرة. وهذا الإجراء الأخير جزئي: يستطيع خادم خبيث الرد بترميز br حتى لو لم يُطلب، ويتبع المفكك رأس الاستجابة. وعلى Vercel، تحجب قواعد WAF التدفق الوارد لكنها لا تحجب الاستجابات التي تجلبها دوالك؛ ويظل إعادة النشر بنسخة مصلَحة ضروريًا.
لا تُصلح ثغرة Brotli وحدها
يصلح الإصدار نفسه تسع ثغرات أخرى. منها ثغرتان عاليتا الخطورة تتجاوزان التحقق من اسم المضيف في TLS، هما CVE-2026-105483 وCVE-2026-106069، إضافة إلى CVE-2026-105482، وهي اقتطاع أسماء المضيفين التي تحوي محرف NUL في تحليلات DNS، ومصنفة High أيضًا. ولا تمسّ كلها الفروع نفسها: تخص CVE-2026-105483 الفرعين 26.x و24.x، وتخص الأخريان 22.x كذلك. فلدى تطبيق يتحقق من شهادات TLS في استدعاءاته الصادرة أسباب وجيهة للانتقال إلى النسخة المصلَحة دون انتظار حسم مسألة Brotli.
لوكالة: إصلاح واحد لكل فرع لا لكل موقع
حين تدير عشرات المواقع، يستغرق أمر التحديث دقيقة واحدة؛ أما الوقت فيضيع في مكان آخر. عليك أن تعرف أي جهاز يشغّل أي نسخة لدى أي عميل، ثم تحصل على موعد لإعادة التشغيل. احتفظ إذن بسجل حي يغذّيه الجرد أعلاه: عند الثغرة التالية، يعطيك مجرد تصفية على فرع Node قائمة المواقع الواجب معالجتها. وكلما قلّت الصيغ المختلفة في المجموعة قلّت الحالات التي تُعالج يدويًا، واستضافة الموزّعين تلبّي هذا المنطق: واجهة واحدة للمحفظة وتجديدات مجمَّعة، بدل مجموعة خوادم متفرقة تضاعف الحسابات الواجب متابعتها. أما التطبيقات العالقة على Node 20 فتحتاج معالجة منفصلة: دليل الترحيل المرتبط أسفل هذا المقال يجنّبك اكتشاف غياب الإصلاح يوم الثغرة التالية.
الوثائق الرسمية
نشرة Node.js الأمنية لشهر أكتوبر 2026: https://nodejs.org/en/blog/vulnerability/october-2026-security-releases
تحليل Rapid7 لـ CVE-2026-111497: https://www.rapid7.com/blog/post/etr-cve-2026-111497-critical-out-of-bounds-write-in-the-node-js-brotli-decoder/
نشرة Vercel: https://vercel.com/kb/bulletin/node-js-security-advisory
وثائق وحدة zlib: https://nodejs.org/api/zlib.html
وثائق nvm: https://github.com/nvm-sh/nvm
تحديث pm2: https://pm2.keymetrics.io/docs/usage/update-pm2/
خيارات docker build: https://docs.docker.com/reference/cli/docker/buildx/build/