مقدمة
شهدت هجمات سلسلة توريد البرمجيات ارتفاعًا ملحوظًا في التكرار والتعقيد خلال السنوات الأخيرة. فبدلاً من مهاجمة التطبيقات مباشرة، يستهدف المهاجمون بشكل متزايد طبقات تحليل التبعيات وتوزيع المكونات البرمجية التي تشكّل أساس تطوير البرمجيات الحديثة. ومن أكثر التقنيات فعالية في هذا السياق: dependency confusion وartifact poisoning.
تستغل هذه الهجمات حقيقة جوهرية: البرمجيات الحديثة تُجمَّع ولا تُكتب من الصفر. فالتطبيق النموذجي قد يسحب مئات أو آلاف الحزم من مصادر خارجية، وصور الحاويات، وقوالب CI، وإضافات البناء — وكل عنصر منها يمثل حلقة في سلسلة الثقة. وعندما تُخترق أي حلقة، تنهار السلسلة بأكملها.
ما يجعل هذه الهجمات خطيرة بشكل خاص هو أن كثيرًا من المؤسسات تظل عرضة لها رغم امتلاكها برامج أمنية ناضجة. فاختبارات أمان التطبيقات التقليدية لا تكشف عن تبعية خبيثة تُثبَّت أثناء البناء. كما أن جدران الحماية وأنظمة WAF لا فائدة منها حين يعمل كود المهاجم داخل خط أنابيب CI/CD الخاص بك مع وصول كامل للشبكة وبيانات اعتماد الإنتاج.
يقدم هذا الدليل فحصًا معمقًا لكيفية عمل dependency confusion وartifact poisoning، ولماذا تُعد خطوط أنابيب CI/CD أهدافًا رئيسية، والأهم من ذلك — ما الدفاعات العملية التي يمكنك تطبيقها اليوم.
شرح Dependency Confusion
كيف تحلّل مديرات الحزم الأسماء
تتبع معظم مديرات الحزم الحديثة — npm وpip وRubyGems وNuGet وMaven — عملية تحليل تتحقق من سجل واحد أو أكثر عند طلب حزمة. وعندما تستخدم مؤسسة ما كلاً من سجل خاص/داخلي (للحزم المملوكة) والسجل العام (لحزم المصدر المفتوح)، يجب على مدير الحزم أن يقرر أي سجل يُعطى الأولوية عندما يحتوي كلاهما على حزمة بنفس الاسم.
يختلف السلوك الافتراضي حسب النظام البيئي، لكن هناك نمط شائع مثير للقلق: كثير من مديرات الحزم يفضّلون رقم الإصدار الأعلى بغض النظر عن السجل الذي يأتي منه. وهذا الخيار التصميمي البريء ظاهريًا هو أساس هجوم dependency confusion.
البحث الأصلي: Alex Birsan (2021)
في فبراير 2021، نشر الباحث الأمني Alex Birsan بحثًا رائدًا يوضح كيف يمكن تسليح سلوك التحليل هذا. من خلال فحص أسماء الحزم الداخلية المسرّبة علنًا من شركات مثل Apple وMicrosoft وTesla — والتي وُجدت في ملفات JavaScript ومانيفستات الحزم ورسائل الخطأ — قام بتسجيل حزم بأسماء مطابقة على سجلات npm وPyPI وRubyGems العامة بأرقام إصدارات مضخّمة.
كانت النتيجة مدمرة. فعندما حلّلت أنظمة البناء في هذه الشركات التبعيات، جلبت مديرات الحزم حزم Birsan العامة بدلاً من الحزم الداخلية المشروعة. تضمنت حزم إثبات المفهوم الخاصة به استدعاءات عودية غير ضارة أكدت تنفيذ الكود داخل الشبكات المؤسسية. حصل Birsan على أكثر من 130,000 دولار في مكافآت الأخطاء عبر عدة مؤسسات.
آليات الهجوم
يتبع هجوم dependency confusion تسلسلاً مباشرًا:
- الاستطلاع: يحدد المهاجم أسماء الحزم الداخلية/الخاصة المستخدمة لدى المؤسسة المستهدفة. يمكن العثور عليها في ملفات
package.jsonالمسرّبة، وخرائط مصدر JavaScript، ورسائل الخطأ، وإعلانات الوظائف، أو مستودعات المصدر المفتوح التي تشير إلى تبعيات داخلية. - التسجيل: يسجّل المهاجم حزمة بنفس الاسم على السجل العام المقابل (npm أو PyPI أو غيره)، مع تعيين رقم إصدار مرتفع جدًا (مثل
99.0.0). - الحمولة: تحتوي الحزمة العامة على كود خبيث في نصوص التثبيت أو خطافات ما بعد التثبيت — كود يُنفَّذ تلقائيًا عند تثبيت الحزمة.
- التنفيذ: عندما يشغّل نظام البناء لدى المؤسسة المستهدفة أمر
npm installأوpip installأو ما يعادله، يحلّل مدير الحزم التبعية إلى حزمة المهاجم العامة بسبب رقم الإصدار الأعلى. - الاختراق: يعمل نص التثبيت الخبيث بصلاحيات عملية البناء، وعادةً ما يحصل على وصول إلى متغيرات البيئة (بما فيها الأسرار)، وموارد الشبكة، والكود المصدري.
الأنظمة البيئية المتأثرة
لا يقتصر dependency confusion على لغة أو نظام بيئي واحد. فالأنظمة التالية جميعها عرضة للخطر:
- npm (Node.js): السلوك الافتراضي قد يفضّل الحزم العامة على الخاصة عند عدم استخدام النطاقات.
- PyPI (Python): علم
--extra-index-urlفي pip يتحقق من الفهرسين الخاص والعام، ويفضّل الإصدار الأعلى. - RubyGems (Ruby): سلوك تحليل مشابه عند تكوين مصادر متعددة.
- NuGet (.NET): يتحقق من عدة تغذيات مكوّنة ويمكن أن يفضّل المعرض العام.
- Maven (Java): يحلّل من مستودعات متعددة؛ يمكن للمهاجمين النشر على Maven Central بمعرّفات group/artifact مطابقة.
نصوص التثبيت كناقل للحمولة
السبب في خطورة dependency confusion هو أن مديرات الحزم تدعم التنفيذ التلقائي للكود أثناء التثبيت. في npm، يحدث هذا من خلال نصوص preinstall وinstall وpostinstall المحددة في package.json. في Python، يمكن لـ setup.py تنفيذ كود عشوائي أثناء pip install. صُممت هذه الخطافات لمهام بناء مشروعة لكنها توفر للمهاجمين ناقل تنفيذ مثالي — ينفَّذ الكود قبل حتى بناء التطبيق، وغالبًا بصلاحيات مرتفعة.
Artifact Poisoning: ما وراء الحزم
بينما يستهدف dependency confusion سجلات الحزم تحديدًا، فإن artifact poisoning هو فئة أوسع من هجمات سلسلة التوريد يمكن أن تستهدف أي مكون خارجي يُستهلك أثناء دورة حياة تطوير البرمجيات. يمتد سطح الهجوم إلى ما هو أبعد بكثير من مديرات الحزم.
صور الحاويات الأساسية المخترقة
تُعد صور الحاويات المسحوبة من Docker Hub أو سجلات عامة أخرى ناقل هجوم شائعًا. يمكن للمهاجم نشر صورة خبيثة باسم مشابه لصورة أساسية شائعة، أو اختراق صورة موجودة بالحصول على وصول إلى حساب المشرف. إذا حدد ملف Dockerfile الخاص بك FROM python:3.11 باستخدام وسم قابل للتغيير، فإن صورة مخترقة تُرفع بهذا الوسم ستُسحب في كل عملية بناء لاحقة.
قوالب CI/CD المعدّلة
تمثل قوالب GitHub Actions وGitLab CI المُشار إليها من مستودعات عامة سطح هجوم كبيرًا آخر. عندما يشير سير العمل إلى uses: some-org/some-action@main، فإن الكود المنفَّذ في خط أنابيبك يتحكم فيه كل من لديه صلاحية الدفع إلى ذلك المستودع. إذا اختُرق مستودع الإجراء، يُخترق كل خط أنابيب يشير إليه أيضًا.
Typosquatting
تستغل هجمات typosquatting الأخطاء الإملائية الشائعة والتشابهات البصرية في أسماء الحزم. من الأمثلة تسجيل co1ors (بالرقم واحد) بدلاً من colors، وlodahs بدلاً من lodash، أو reqeusts بدلاً من requests. تحتوي هذه الحزم على كود خبيث وتعتمد على وقوع المطورين في أخطاء مطبعية عند إضافة التبعيات. اكتشفت الأدوات الآلية آلاف حزم typosquatting عبر npm وPyPI.
حسابات المشرفين المخترقة
عندما يحصل مهاجم على وصول إلى حساب مشرف حزمة مشروع — من خلال حشو بيانات الاعتماد أو التصيد الاحتيالي أو الهندسة الاجتماعية — يمكنه نشر إصدارات تحتوي على أبواب خلفية من حزم مستخدمة على نطاق واسع. ولأن اسم الحزمة والمشرف مشروعان، فإن هذه الإصدارات المخترقة صعبة الكشف للغاية بالوسائل الآلية.
إضافات أدوات البناء
تدعم أنظمة البناء مثل Gradle وMaven وwebpack إضافات تُنفَّذ أثناء عملية البناء. يمكن للإضافات الخبيثة أو المخترقة في هذه الأنظمة تعديل مخرجات البناء، أو تسريب الأسرار، أو حقن أبواب خلفية في المكونات المُجمَّعة. ولأن إضافات البناء غالبًا ما تخضع لفحص أقل من تبعيات التطبيق، فإنها تمثل هدفًا عالي القيمة.
حوادث واقعية
توضح عدة حوادث كبرى التأثير الواقعي لـ artifact poisoning:
- event-stream (2018): مُنح مشرف جديد صلاحيات النشر لحزمة npm الشهيرة
event-stream(1.5 مليون تنزيل أسبوعي). أضاف تبعية على حزمة خبيثة،flatmap-stream، التي احتوت على كود مشفّر يستهدف محفظة Copay Bitcoin، محاولاً سرقة العملات المشفرة. - ua-parser-js (2021): اختُرقت حزمة npm
ua-parser-js(7 ملايين تنزيل أسبوعي) عند اختراق حساب المشرف. نُشرت إصدارات خبيثة ثبّتت برامج تعدين العملات المشفرة وبرامج سرقة بيانات الاعتماد على أنظمة Linux وWindows. - node-ipc (2022): أضاف مشرف حزمة
node-ipcعمدًا كودًا يمسح الملفات على الأنظمة ذات عناوين IP الروسية أو البيلاروسية، مما يوضح أن حتى المشرفين الموثوقين يمكن أن يصبحوا ناقل تهديد (يُطلق عليه أحيانًا “protestware”).
كيف تستغل هذه الهجمات CI/CD
خطوط أنابيب CI/CD عرضة بشكل فريد لهجمات dependency confusion وartifact poisoning بسبب طريقة تصميمها للعمل. فهم لماذا تُعد خطوط الأنابيب أهدافًا رئيسية أمر ضروري لبناء دفاعات فعالة.
التنفيذ التلقائي للكود أثناء البناء
في كل مرة يشغّل فيها خط أنابيب CI أمر npm install أو pip install -r requirements.txt أو docker build، فإنه ينفّذ كودًا من مصادر خارجية. يحدث هذا تلقائيًا مع كل commit أو pull request أو بناء مجدول. لا يوجد إنسان في الحلقة لمراجعة الكود الذي يُسحب ويُنفَّذ فعليًا.
بيئات البناء لديها وصول إلى بيانات الاعتماد
عادةً ما تُكوَّن بيئات CI/CD بأسرار مطلوبة للنشر: بيانات اعتماد مزوّد السحابة، ورموز API، وكلمات مرور قواعد البيانات، ومفاتيح التوقيع، وبيانات اعتماد السجلات. يمكن لتبعية خبيثة تعمل أثناء مرحلة البناء الوصول إلى هذه الأسرار عبر متغيرات البيئة أو مخازن الأسرار المُركَّبة. وهذا يجعل خطوط أنابيب CI/CD أهدافًا أكثر قيمة بكثير من حواسيب المطورين المحمولة.
التبعيات المجلوبة وقت البناء لا تخضع لتدقيق مسبق
في معظم المؤسسات، تُحدَّد إصدارات التبعيات في ملفات المانيفست (package.json، requirements.txt) لكن الكود الفعلي يُجلب حديثًا من السجلات وقت البناء. بين وقت إضافة مطور لتبعية ووقت تثبيت نظام CI لها، قد تتغير محتويات الحزمة — أو قد تظهر حزمة dependency confusion على السجل العام. عادةً لا توجد خطوة تحقق بين التحليل والتنفيذ.
التبعيات العابرة توسّع سطح الهجوم
قد يُعلن تطبيقك عن 50 تبعية مباشرة، لكن لهذه التبعيات تبعياتها الخاصة، مما يُنشئ شجرة قد تتضمن آلاف الحزم العابرة. ليس لديك تحكم مباشر فيما تعتمد عليه تبعياتك. عندما تُخترق تبعية عابرة — كما في حادثة event-stream — ينتشر الهجوم عبر شجرة التبعيات بأكملها دون أي تغيير في ملفات المانيفست الخاصة بك.
الدفاع ضد Dependency Confusion
يتطلب منع dependency confusion تكوين مديرات الحزم والسجلات لديك للقضاء على الغموض بين الحزم العامة والخاصة. إليك أكثر التخفيفات فعالية.
استخدم النطاقات لحزمك الخاصة
الدفاع الأكثر فعالية هو استخدام أسماء حزم ذات نطاقات لجميع الحزم الداخلية. في npm، يعني هذا استخدام حزم محددة النطاق مثل @yourcompany/package-name. لا يمكن للمهاجم تسجيل حزم تحت نطاق مؤسستك على سجل npm العام.
كوِّن أولوية السجل صراحةً
لا تعتمد أبدًا على سلوك التحليل الافتراضي. كوِّن مدير الحزم صراحةً لجلب الحزم ذات النطاق من سجلك الخاص وكل شيء آخر من السجل العام.
مثال على تكوين .npmrc:
# Always fetch @yourcompany scoped packages from private registry
@yourcompany:registry=https://npm.yourcompany.com/
# All other packages come from the public npm registry
registry=https://registry.npmjs.org/
# Authentication for private registry
//npm.yourcompany.com/:_authToken=${NPM_PRIVATE_TOKEN}
مثال على pip.conf لـ Python:
# IMPORTANT: Use --index-url (NOT --extra-index-url) for your private registry
# --extra-index-url checks BOTH registries and picks the higher version (vulnerable!)
# --index-url uses ONLY your private registry as the primary source
[global]
index-url = https://pypi.yourcompany.com/simple/
# If you need public PyPI packages, configure your private registry
# (Artifactory, Nexus) to proxy public PyPI — do NOT use --extra-index-url
مثال على .yarnrc.yml لـ Yarn Berry:
npmScopes:
yourcompany:
npmRegistryServer: "https://npm.yourcompany.com"
npmAuthToken: "${NPM_PRIVATE_TOKEN}"
npmRegistryServer: "https://registry.yarnpkg.com"
استخدم وكلاء السجلات الخاصة
انشر وكيل سجل مثل JFrog Artifactory أو Sonatype Nexus أو GitHub Packages يقع بين أنظمة البناء والسجلات العامة. كوِّن الوكيل لـ:
- تقديم الحزم الداخلية من مستودعك الخاص.
- توكيل الحزم العامة من السجل الأصلي.
- حظر أي حزمة عامة تشترك في الاسم مع حزمة داخلية.
- تطبيق سياسات الأمان (فحص الثغرات، التوافق مع التراخيص) قبل السماح بمرور الحزم.
هذا يُنشئ مصدرًا واحدًا للحقيقة لجميع التبعيات ويزيل الغموض بين العام والخاص تمامًا.
التسجيل الدفاعي
سجّل أسماء حزمك الداخلية على السجلات العامة كحزم مُعلَّقة. يجب ألا تحتوي هذه الحزم على كود حقيقي — فقط ملف README يشرح أن الاسم محجوز. هذا يمنع المهاجمين من المطالبة بتلك الأسماء. رغم أن هذا ليس دفاعًا أساسيًا، إلا أنه يضيف طبقة حماية إضافية.
ثبّت التبعيات بالتجزئة
تثبيت التبعيات بتجزئة تشفيرية يضمن أن المكون الذي راجعته بالضبط هو ما يُثبَّت أثناء البناء. حتى لو نشر مهاجم إصدارًا خبيثًا، لن تتطابق التجزئة وسيفشل التثبيت.
لـ pip (Python):
# Generate hashes for your requirements
pip-compile --generate-hashes requirements.in -o requirements.txt
# Install with hash verification
pip install --require-hashes -r requirements.txt
لـ npm:
يسجل npm تلقائيًا تجزئات السلامة في package-lock.json. تأكد من إيداع ملف القفل واستخدام npm ci (وليس npm install) في CI لفرض التحقق من السلامة:
# In CI, always use npm ci — it strictly follows the lockfile
# and verifies integrity hashes for every package
npm ci
الدفاع ضد Artifact Poisoning
لأن artifact poisoning يشمل نطاقًا أوسع من نواقل الهجوم، يتطلب الدفاع ضوابط متعددة الطبقات عبر صور الحاويات وقوالب CI والتبعيات وعمليات البناء.
ثبّت صور الحاويات بالملخص
لا تشر أبدًا إلى صور الحاويات بوسوم قابلة للتغيير مثل latest أو حتى 3.11. بدلاً من ذلك، ثبّت على ملخص SHA256 غير القابل للتغيير:
# Vulnerable: tag can be overwritten with a compromised image
FROM python:3.11-slim
# Secure: digest is immutable — this exact image or nothing
FROM python:3.11-slim@sha256:a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
ثبّت GitHub Actions بـ SHA
أشر إلى GitHub Actions بتجزئة الـ commit الكاملة بدلاً من وسم قابل للتغيير:
# Vulnerable: v3 tag can be moved to point to compromised code
- uses: actions/checkout@v3
# Secure: pinned to specific commit SHA
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
تحقق من التوقيعات على التبعيات
حيثما توفّر، تحقق من التوقيعات التشفيرية لتأكيد أن المكونات نُشرت من قبل مشرفيها المتوقعين:
- npm: استخدم
npm audit signaturesللتحقق من توقيعات السجل على الحزم. - صور الحاويات: استخدم Sigstore/cosign للتحقق من توقيعات صور الحاويات.
- Python: PEP 740 وأدوات مثل
sigstore-pythonتجلب التحقق من التوقيعات إلى PyPI.
أنشئ وتتبّع SBOMs
يُحصي SBOM (قائمة مواد البرمجيات) كل مكون في تطبيقك. من خلال إنشاء SBOMs لكل عملية بناء ومقارنتها، يمكنك اكتشاف الإضافات أو التغييرات غير المتوقعة في شجرة التبعيات. يمكن لأدوات مثل Syft وTrivy وCycloneDX إنشاء SBOMs بتنسيقات معيارية (SPDX، CycloneDX).
أتمت مراجعة التبعيات
انشر أدوات آلية تفحص تبعياتك باستمرار بحثًا عن الثغرات المعروفة والتغييرات المشبوهة:
- Dependabot / Renovate: ينشئان تلقائيًا طلبات سحب عند توفر تحديثات التبعيات، مما يمنحك فرصة للمراجعة قبل الدمج.
- npm audit / pip-audit: يفحصان الثغرات المعروفة في شجرة التبعيات.
- GitHub Dependency Review Action: يحظر طلبات السحب التي تُدخل تبعيات ذات ثغرات معروفة.
قيّد وصول الشبكة أثناء البناء (Hermetic Builds)
البناء المحكم هو بناء لا يمكنه الوصول إلى الشبكة. يجب جلب جميع التبعيات وتخزينها مؤقتًا قبل بدء البناء. هذا يمنع البناء من جلب حزمة خبيثة منشورة حديثًا. يدعم Bazel البناء المحكم أصلاً، ويمكن تحقيق عزل مماثل بعلم --network=none في Docker أو سياسات الشبكة الخاصة بمنصة CI.
اعتمد مسبقًا وأنشئ قوائم مسموحة للتبعيات
حافظ على قائمة معتمدة من التبعيات وإصداراتها. أي تبعية جديدة أو تغيير في الإصدار يتطلب موافقة صريحة من خلال عملية مراجعة. رغم أن هذا يضيف احتكاكًا، إلا أنه يمنع الحزم غير المصرح بها من الدخول إلى خط أنابيب البناء.
الكشف والمراقبة
حتى مع ضوابط وقائية قوية، تُعد قدرات الكشف ضرورية لالتقاط الهجمات التي تتجاوز دفاعاتك.
راقب التبعيات الجديدة غير المتوقعة
طبّق فحوصات CI تُنبّه على طلبات السحب التي تُدخل تبعيات جديدة. اطلب مبررًا ومراجعة صريحين لأي إضافة إلى مانيفست التبعيات. هذا مهم بشكل خاص للتبعيات العابرة — فتبعية مباشرة جديدة قد تجلب عشرات التبعيات العابرة.
أنذر عند قفزات إصدار التبعيات
تبعية تقفز من الإصدار 1.2.3 إلى 99.0.0 هي مؤشر قوي على هجوم dependency confusion. طبّق مراقبة تُنذر عند تغييرات الإصدار غير المعتادة، خاصة القفزات الكبيرة في الإصدار الرئيسي للحزم الداخلية.
استفد من أدوات الفحص الأمني
- Socket.dev: يحلل سلوك الحزمة (الوصول إلى الشبكة، الوصول إلى نظام الملفات، نصوص التثبيت) بدلاً من مجرد CVEs المعروفة، مما يجعله فعالاً في كشف هجمات سلسلة التوريد.
- Snyk: يوفر فحص ومراقبة الثغرات عبر أنظمة بيئية متعددة.
- GitHub Dependency Graph وDependabot Alerts: يتتبعان التبعيات تلقائيًا ويُنذران عند الثغرات المعروفة.
افحص نصوص التثبيت وخطافات ما بعد التثبيت
طبّق أدوات تحدد تحديدًا الحزم التي تحتوي على نصوص تثبيت. رغم أن نصوص التثبيت لها استخدامات مشروعة، إلا أنها ناقل التنفيذ الرئيسي لهجمات dependency confusion. ضع علامة وراجع أي حزمة تتضمن نصوص preinstall أو install أو postinstall في npm، أو setup.py بكود قابل للتنفيذ في Python.
قارن SBOMs بين عمليات البناء
قارن بانتظام SBOMs من عمليات البناء المتتالية. التغييرات غير المتوقعة — حزم جديدة تظهر، إصدارات تتغير دون تحديثات مانيفست مقابلة، أو حزم من سجلات غير متوقعة — يجب أن تُطلق تنبيهات وتحقيقات.
تقوية CI/CD بشكل خاص
بالإضافة إلى إدارة التبعيات، يحتاج تكوين خط أنابيب CI/CD نفسه إلى تقوية لمقاومة هجمات سلسلة التوريد.
أودع وتحقق من ملفات القفل في CI
تسجّل ملفات القفل (package-lock.json، yarn.lock، Pipfile.lock، poetry.lock) الإصدارات الدقيقة وتجزئات كل تبعية. يجب أن يفشل خط أنابيب CI إذا كان ملف القفل يحتوي على تغييرات غير متوقعة.
مثال GitHub Actions — التحقق من سلامة ملف القفل:
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Setup Node.js
uses: actions/setup-node@1a4442cacd436585916f1b3aa94e4166f1a22160 # v3.8.2
with:
node-version: '20'
- name: Verify lockfile has not been tampered with
run: |
# npm ci will fail if package-lock.json is out of sync
# with package.json or if integrity hashes don't match
npm ci
- name: Check for lockfile modifications
run: |
if ! git diff --exit-code package-lock.json; then
echo "ERROR: package-lock.json was modified during install."
echo "This could indicate a dependency confusion attack."
exit 1
fi
- name: Audit dependencies
run: npm audit --audit-level=high
مثال GitLab CI — التحقق من ملف القفل مع pip:
stages:
- verify
- build
- test
verify-dependencies:
stage: verify
image: python:3.11-slim@sha256:abc123... # Pin by digest
script:
- pip install pip-tools pip-audit
# Verify that requirements.txt hashes match actual packages
- pip install --require-hashes --no-deps -r requirements.txt
# Audit for known vulnerabilities
- pip-audit -r requirements.txt
# Ensure no unexpected changes to lockfile
- pip-compile --generate-hashes requirements.in -o /tmp/requirements-check.txt
- diff requirements.txt /tmp/requirements-check.txt
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
build:
stage: build
image: python:3.11-slim@sha256:abc123...
script:
- pip install --require-hashes --no-deps -r requirements.txt
- python -m build
needs: [verify-dependencies]
افصل تحليل التبعيات عن تنفيذ البناء
استخدم عملية بناء من مرحلتين: أولاً حلّل ونزّل التبعيات في بيئة معزولة، ثم شغّل البناء الفعلي مع تعطيل الوصول إلى الشبكة. هذا يمنع تبعية مخترقة من تسريب البيانات أو تنزيل حمولات إضافية أثناء البناء.
بيئات بناء معزولة عن الشبكة
للبناءات عالية الأمان، استخدم بيئات مقيّدة الشبكة لا يمكنها الوصول إلا إلى وكيل السجل الداخلي. هذا يلغي إمكانية جلب التبعيات مباشرة من السجلات العامة أثناء البناء.
# Docker-based hermetic build example
# Phase 1: Fetch dependencies (with network)
docker run --name dep-fetch my-builder:latest \
npm ci --prefer-offline
# Phase 2: Build (without network)
docker run --network=none -v deps:/app/node_modules \
my-builder:latest npm run build
خزّن التبعيات في تخزين داخلي موثوق
بدلاً من جلب التبعيات من السجلات العامة في كل عملية بناء، خزّن الإصدارات المعتمدة في تخزين داخلي (Artifactory أو Nexus أو دلاء التخزين السحابي). يجب أن يسحب خط أنابيب CI حصريًا من هذه الذاكرة المؤقتة الموثوقة. حدّث الذاكرة المؤقتة من خلال عملية خاضعة للرقابة والتدقيق.
الخلاصة
يستغل dependency confusion وartifact poisoning افتراضات ثقة راسخة في سلسلة توريد البرمجيات. كل أمر npm install، وكل docker pull، وكل توجيه uses: في سير عمل GitHub Actions هو قرار ثقة — والمهاجمون يعملون بنشاط لإساءة استخدام تلك الثقة.
الدفاع الفعال ليس أداة واحدة أو تغيير تكوين واحد. بل يتطلب ضوابط في كل طبقة من دورة حياة التبعيات:
- التحليل: حدد نطاقات لحزمك، كوِّن أولوية السجل، استخدم سجلات وكيلة للقضاء على الغموض بين العام والخاص.
- التثبيت: ثبّت بالتجزئة، استخدم ملفات القفل مع
npm ciأو--require-hashes، عطّل نصوص التثبيت حيثما أمكن. - التحقق: تحقق من التوقيعات، قارن SBOMs، دقّق التبعيات قبل دخولها البناء.
- المراقبة: اكشف تغييرات التبعيات غير المتوقعة، أنذر عند شذوذات الإصدار، افحص السلوك الخبيث في الحزم.
- تقوية خط الأنابيب: قيّد وصول الشبكة أثناء البناء، تحقق من سلامة ملف القفل في CI، افصل تحليل التبعيات عن تنفيذ البناء.
المؤسسات الأكثر مرونة تجاه هجمات سلسلة التوريد هي تلك التي تعامل التبعيات بنفس الصرامة التي تطبقها على كودها الخاص: مُراجَعة، ومُتحقَّق منها، ومُراقَبة، ولا تُمنح الثقة ضمنيًا أبدًا. ابدأ بتطبيق الضوابط الأعلى تأثيرًا — الحزم ذات النطاقات، تكوين السجل، التحقق من ملف القفل في CI — ثم أضف طبقات تدريجيًا مع نضوج برنامج أمان سلسلة التوريد لديك.