245 مليون تنزيل خلال 86 دقيقة: هجوم سلسلة التوريد على حزمة arrayref
- Home
- المدونة
أنا جو. في 20 أغسطس 2026 نشر أحدهم إصدارًا مسمومًا من حزمة Rust لا يفكر فيها أحد تقريبًا ويترجمها الجميع تقريبًا. لم يكن في الهجوم شيء مبتكر — ولهذا بالضبط يستحق التدوين. فقد استخدم هوية نشر مسروقة، واسمًا يشبه اسمًا تثق به أصلًا، ونص بناء. وفيما يلي ما حدث، وما تعنيه الأرقام فعليًا، وحفنة الضوابط التي تقرر ما إذا كان حادث كهذا هامشًا في سجلات التكامل المستمر لديك أم واقعة سرقة بيانات اعتماد.
-
ما الذي حدث فعلًا
في 20 أغسطس 2026 ظهر إصدار مخترَق من arrayref 0.3.10 على crates.io، إلى جانب internment 0.8.7 وappend-only-vec 0.1.9. وأضاف كل منها اعتمادية على حزمة منتحلة الاسم تُدعى proc-macro1 — وهو اسم اختير ليبعد حرفًا واحدًا عن حزمة يسحبها كل مشروع Rust تقريبًا. وقد نزّل نص بناء تلك الحزمة ملفًا تنفيذيًا بعيدًا وشغّله أثناء ترجمة المشروع. ويبدو أن حساب المشرف وراء اثنتين من الحزم المتأثرة قد اختُرق لا أنه كان متواطئًا؛ فسحب فريق الاستجابة الأمنية في Rust الإصدارات، وأقفل الحساب، وحذف ست حزم أنشأها المهاجم (proc-macro1 وproc-macro-en وaovine وarone وaronenao وtinymember).
-
وقت البناء أسوأ من وقت التشغيل
هذا هو الجزء الذي تستهين به الفرق. لست مضطرًا لتشغيل البرنامج كي تعمل برمجية خبيثة تعمل وقت البناء — فأمر cargo build يكفي. أي أن الشفرة تُنفَّذ على حواسيب المطورين، والأهم بكثير، داخل منفّذي التكامل المستمر: الأجهزة التي تحمل رموز السجلات، وبيانات اعتماد السحابة، ومفاتيح التوقيع، ووصول SSH إلى كل ما تنشره. الباب الخلفي وقت التشغيل يحتاج مستخدميك؛ أما الباب الخلفي وقت البناء فلا يحتاج سوى خط إنتاجك. وخط الإنتاج هو المكان الوحيد في معظم المؤسسات الذي تجلس فيه بيانات اعتماد عالية الامتياز بجوار شفرة طرف ثالث اعتباطية، بالتصميم.
-
الأرقام، وما لا تعنيه
لحزمة arrayref نحو 245 مليون تنزيل منذ إطلاقها، وقرابة 53.7 مليون خلال التسعين يومًا الأخيرة، و403 حزم تدرجها كاعتمادية مباشرة — والعدد غير المباشر أعلى بكثير. هذه الأرقام تصف نطاق ضرر الاسم، لا عدد الضحايا. أما ما يقرر ما إذا كنت منكشفًا فأضيق بكثير: هل حلّ أي بناء في بيئتك شجرة اعتماديات جديدة خلال تلك النافذة، وهل التقط الإصدار الفاسد؟ فوجود ملف اعتماديات مقفل ومُودَع في المستودع يعني أن الجواب شبه المؤكد لا.
-
ستٌ وثمانون دقيقة ليست «لا شيء»
نُشر إصدار arrayref الفاسد الساعة 07:15 بالتوقيت العالمي واختفى من الفهرس بحلول 08:41 — أي نافذة انكشاف مدتها 86 دقيقة، مع بقاء الإصدارات المتأثرة الأخرى في المدى نفسه بين 86 و107 دقائق. هذه استجابة سريعة فعلًا، ومع ذلك ليست قصيرة. فتسعون دقيقة صباح اعتيادي في التكامل المستمر: روبوتات تحديث الاعتماديات، وعمليات إعادة بناء ليلية، وطابور دمج، وخط إصدار يعيد الحل من الصفر داخل حاوية نظيفة. الأتمتة لا تنتظر الإرشاد الأمني. وحين يقول الناس إن النافذة كانت صغيرة، فهم يقيسون بالزمن البشري؛ أما المهاجمون فيقيسون بزمن البناء.
-
كيف أفرز هذا في بيئة عميل
السؤال الأول ليس أبدًا «هل نحن متأثرون»، بل «هل نستطيع الإجابة عن ذلك السؤال أصلًا». أقارن ملف القفل المُودَع بما حلّته آخر عمليات بناء فعليًا، ثم أبحث في سجلات التكامل المستمر عن الإصدارات المتأثرة بالسلسلة النصية الدقيقة. ثم أبحث عن اتصالات شبكية صادرة أثناء خطوة بناء، لأن الترجمة لا سبب مشروع لديها لجلب ملف تنفيذي. ثم أفحص ما كان بوسع تلك المنفّذات الوصول إليه: أي رموز كانت في البيئة، وما نطاقها، وهل جرى تدويرها بعد النافذة. وإن كان الجواب عن أي من ذلك «لا نحتفظ بتلك السجلات»، فتلك الفجوة هي النتيجة — وستظل هي النتيجة في الحادث القادم.
-
الضوابط التي كانت ستحتويه
أودِع ملف القفل في المستودع وابنِ بخيار --locked في التكامل المستمر، كي لا يُسحب إصدار خبيث جديد بصمت. وشغّل عمليات البناء في بيئة معزولة بلا وصول شبكي صادر ما لم تحتج خطوة ما إليه فعلًا — فهذا الضابط وحده يُبطل معظم مُسقطات نصوص البناء. وتعامل مع نصوص البناء كشفرة غير موثوقة، لأنها كذلك بالضبط. وامنح كل خط إنتاج أضيق بيانات اعتماد يستطيع العمل بها، وفضّل الرموز قصيرة الأجل على مفاتيح السجلات طويلة الأمد. وضع نسخًا محلية أو مرايا للاعتماديات في كل ما تشحنه إلى العملاء. لا شيء من هذا غريب؛ وكله غير براق، وهو الفارق بين تنبيه وحادث.
-
المشرف جزء من سطح هجومك
لم يكن الخلل التقني هنا في Rust ولا في cargo ولا في الحزمة نفسها. بل في بيانات نشر شخص واحد. فكل سجل تعتمد عليه هو في النهاية قائمة حسابات فردية تستطيع دفع شفرة إلى عملية بنائك. تستطيع السجلات تقليص هذه المخاطرة بفرض المصادقة الثنائية إلزاميًا عند النشر، ورموز نشر محدودة النطاق وقصيرة الأجل، وخطوة تأخير أو إثبات على الإصدارات الجديدة للحزم واسعة الانتشار. ويستطيع المستهلكون تقليصها بألا يعاملوا «الأحدث» كقيمة بحد ذاتها. وقد لاحظ باحثون تداخلًا في البنية التحتية بين هذه الحملة ونشاط سبق ربطه بجهات حكومية؛ وهذا سياق مفيد، لكن الإسناد لم يرقّع شيئًا قط.
-
قائمة التحقق الخاصة بك لهذا الأسبوع
ابحث في كل ملف قفل وكل سجل تكامل مستمر عن arrayref 0.3.10 وinternment 0.8.7 وappend-only-vec 0.1.9، وعن أي إشارة إلى proc-macro1. وإن وجدت واحدة، فدوّر كل بيانات اعتماد كان بوسع ذلك المنفّذ رؤيتها، وعامل مضيف البناء كمخترَق حتى تثبت العكس. ثم أجرِ التغيير البنيوي لا التكتيكي فقط: اقفل الاعتماديات وأودعها، واحجب الحركة الصادرة أثناء البناء، وقصّر عمر بيانات اعتماد التكامل المستمر لديك. فالحزمة المسمومة القادمة ستحمل اسمًا مختلفًا، ولن يعبأ أي من هذه الضوابط الثلاثة بذلك.
التعليقات (5)
-
ci_greg قبل يومين ردنصيحة حجب الحركة الصادرة هي التي حازت الموافقة أخيرًا في شركتي. تبيّن أن لا شيء تقريبًا في عملية بنائنا كان يحتاج الإنترنت فعلًا بعد خطوة الجلب.
-
هكذا يجري الأمر عادة. قائمة الاستثناءات دائمًا أقصر مما يتوقع الناس، وتدوينها نصف القيمة.
-
-
86 دقيقة زمن استجابة مثير للإعجاب فعلًا من الفريق الأمني. الناس يظلمونهم في النقاشات.
-
كانت لدينا مهمة ليلية تشغّل cargo update. لم نجد شيئًا في السجلات، لكن فقط لأننا صادف أننا نحتفظ بها 30 يومًا. كان ذلك حظًا، لا تصميمًا.
-
اكتب تلك الجملة حرفيًا في مراجعة المخاطر القادمة لديك. «استطعنا الإجابة بالحظ» هي النتيجة.
-
-
فرض المصادقة الثنائية إلزاميًا عند النشر للحزم عالية التنزيل يبدو الخطوة البديهية. أتساءل لماذا لا تزال السجلات بطيئة في هذا.