مختبر: محاكاة هجوم Dependency Confusion في بيئة Sandbox

نظرة عامة

يُعد dependency confusion هجومًا على سلسلة التوريد يستغل الطريقة التي تحل بها أدوات إدارة الحزم أسماء الحزم عند تكوين كل من السجلات الخاصة (الداخلية) والعامة. عندما ينشر مهاجم حزمة خبيثة على سجل عام باستخدام نفس اسم حزمة خاصة داخلية — ولكن برقم إصدار أعلى — قد يفضل مدير الحزم الإصدار العام، مما يؤدي إلى سحب كود يتحكم فيه المهاجم بصمت.

اكتسب هذا الأسلوب الهجومي اهتمامًا واسعًا في عام 2021 عندما أثبته الباحث الأمني Alex Birsan ضد Apple وMicrosoft وPayPal وTesla وعشرات المؤسسات الأخرى. المشكلة الجوهرية بسيطة: معظم أدوات إدارة الحزم تختار افتراضيًا أعلى إصدار متاح عبر جميع السجلات المكوّنة.

في هذا المختبر العملي، ستقوم بـ:

  • إعداد بيئة معزولة (sandbox) بسجلين محليين يحاكيان السجل “الخاص” و”العام”
  • تنفيذ هجوم dependency confusion في كل من نظامي npm وpip
  • تطبيق أربع استراتيجيات دفاعية مختلفة والتحقق منها
  • فهم السبب الدقيق لنجاح كل دفاع على مستوى البروتوكول

كل أمر في هذا المختبر مصمم للعمل على البنية التحتية المحلية فقط. لا يتم نشر أي حزم على سجلات عامة حقيقية في أي مرحلة.

المتطلبات الأساسية

قبل بدء هذا المختبر، تأكد من تثبيت الأدوات التالية وتوفرها على جهازك:

  • Node.js 18+ مع npm (تحقق باستخدام node --version و npm --version)
  • Python 3.8+ مع pip (تحقق باستخدام python3 --version و pip3 --version)
  • Docker (يُستخدم لتشغيل نسخ Verdaccio وpypiserver المحلية)
  • curl (لإنشاء مستخدمي السجل)
  • طرفية (terminal) تعمل بـ bash أو zsh

لا حاجة لحسابات سحابية أو خدمات خارجية. المختبر بالكامل يعمل محليًا على محطة العمل الخاصة بك.

ملاحظة أمان مهمة

تحذير: يجب تشغيل هذا المختبر في بيئات اختبار معزولة فقط. كل سجل وحزمة وتكوين في هذا المختبر محلي. التزم بهذه القواعد بصرامة:

  • لا تنشر أبدًا حزم اختبارية على سجلات npmjs.com أو pypi.org الحقيقية. نشر حزم بأسماء تطابق الحزم الداخلية لمؤسسة أخرى غير قانوني في العديد من الولايات القضائية وينتهك شروط خدمة السجل.
  • استخدم فقط نسخ Verdaccio وpypiserver المحلية كسجلاتك “العامة” و”الخاصة”. هذه معزولة تمامًا ولا يمكنها التأثير على العالم الخارجي.
  • جميع الحزم “العامة” في هذا المختبر تُنشر على نسخة Verdaccio محلية ثانية تعمل على localhost:4874. لا شيء يغادر جهازك.
  • لا تُشغّل تمارين الهجوم على مشاريع إنتاجية. استخدم مجلد مشروع جديد يمكن التخلص منه.
  • نظّف جميع الحاويات والتكوينات عند الانتهاء. ترك ملفات .npmrc أو pip.conf مُعدّة بشكل خاطئ على نظامك قد يسبب سلوكًا غير متوقع في المشاريع الحقيقية.

مع وجود هذه الاحتياطات، يمكنك استكشاف آليات هذا الهجوم بأمان وبناء فهم حقيقي للدفاعات.

إعداد البيئة

الخطوة 1: تشغيل نسختين من Verdaccio

Verdaccio هو سجل npm خفيف ومفتوح المصدر. سنشغّل نسختين — واحدة تحاكي السجل الخاص بمؤسستك، وأخرى تحاكي سجلًا عامًا يتحكم فيه المهاجم.

# Start the "private" registry on port 4873
docker run -d -p 4873:4873 --name private-registry verdaccio/verdaccio

# Start the "public" registry on port 4874
docker run -d -p 4874:4873 --name public-registry verdaccio/verdaccio

تحقق من أن كليهما يعمل:

curl -s http://localhost:4873/ | head -5
curl -s http://localhost:4874/ | head -5

يجب أن ترى استجابات HTML من واجهتي Verdaccio.

الخطوة 2: إنشاء مستخدمي السجل

يتطلب Verdaccio المصادقة للنشر. أنشئ مستخدمًا على كل نسخة:

# Add user to private registry
npm adduser --registry http://localhost:4873
# Enter username: testuser, password: testpass, email: test@test.com

# Add user to public registry
npm adduser --registry http://localhost:4874
# Enter username: attacker, password: attackpass, email: attacker@test.com

الخطوة 3: إنشاء مشروع الاختبار

mkdir -p ~/dep-confusion-lab/victim-project
cd ~/dep-confusion-lab/victim-project
npm init -y

الخطوة 4: إنشاء ونشر الحزمة الخاصة

mkdir -p ~/dep-confusion-lab/private-pkg
cd ~/dep-confusion-lab/private-pkg

أنشئ package.json:

{
  "name": "@mycompany/auth-utils",
  "version": "1.0.0",
  "description": "Internal authentication utilities",
  "main": "index.js"
}

أنشئ index.js:

module.exports = {
  validateToken: function(token) {
    console.log('[auth-utils v1.0.0] Validating token (PRIVATE - LEGITIMATE)');
    return token && token.length > 0;
  }
};

انشر على السجل الخاص:

npm publish --registry http://localhost:4873

الخطوة 5: تكوين مشروع الضحية

في مجلد مشروع الضحية، أنشئ ملف .npmrc:

registry=http://localhost:4873

أضف التبعية إلى package.json:

{
  "name": "victim-project",
  "version": "1.0.0",
  "dependencies": {
    "@mycompany/auth-utils": "^1.0.0"
  }
}

ثبّت وتحقق:

npm install
node -e "const auth = require('@mycompany/auth-utils'); auth.validateToken('abc');"

يجب أن ترى: [auth-utils v1.0.0] Validating token (PRIVATE - LEGITIMATE)

التمرين 1: الهجوم — npm

الآن نحاكي ما سيفعله المهاجم. الملاحظة الأساسية: في العديد من التكوينات الواقعية، يستخدم المطورون اسم حزمة بدون نطاق داخليًا (فقط auth-utils بدلاً من @mycompany/auth-utils). هذا يجعل الهجوم سهلاً للغاية.

الخطوة 1: إعادة تعيين مشروع الضحية لاستخدام اسم بدون نطاق

حدّث package.json لمشروع الضحية ليعتمد على الاسم بدون نطاق:

{
  "name": "victim-project",
  "version": "1.0.0",
  "dependencies": {
    "auth-utils": "^1.0.0"
  }
}

وانشر أيضًا auth-utils@1.0.0 بدون نطاق على السجل الخاص:

mkdir -p ~/dep-confusion-lab/private-pkg-unscoped
cd ~/dep-confusion-lab/private-pkg-unscoped
// package.json
{
  "name": "auth-utils",
  "version": "1.0.0",
  "description": "Internal authentication utilities (unscoped)",
  "main": "index.js"
}

// index.js
module.exports = {
  validateToken: function(token) {
    console.log('[auth-utils v1.0.0] Validating token (PRIVATE - LEGITIMATE)');
    return token && token.length > 0;
  }
};
npm publish --registry http://localhost:4873

الخطوة 2: إنشاء الحزمة الخبيثة

mkdir -p ~/dep-confusion-lab/malicious-pkg
cd ~/dep-confusion-lab/malicious-pkg

أنشئ package.json — لاحظ رقم الإصدار المرتفع للغاية وسكربت postinstall:

{
  "name": "auth-utils",
  "version": "99.0.0",
  "description": "Malicious package simulating dependency confusion",
  "main": "index.js",
  "scripts": {
    "postinstall": "node malicious.js"
  }
}

أنشئ malicious.js — يحاكي هذا استخراج البيانات عن طريق كتابة ملف تعريفي:

const fs = require('fs');
const os = require('os');
const path = require('path');

const marker = path.join(os.homedir(), 'dep-confusion-lab', 'ATTACK_MARKER.txt');
const data = [
  'DEPENDENCY CONFUSION ATTACK SIMULATION',
  '=======================================',
  `Timestamp: ${new Date().toISOString()}`,
  `Hostname: ${os.hostname()}`,
  `Username: ${os.userInfo().username}`,
  `Working Directory: ${process.cwd()}`,
  '',
  'In a real attack, this script could:',
  '  - Exfiltrate environment variables (API keys, tokens)',
  '  - Upload source code to an external server',
  '  - Install a reverse shell or backdoor',
  '  - Modify build outputs'
].join('\n');

fs.writeFileSync(marker, data);
console.log('[!] ATTACK SIMULATION: Marker file written to', marker);

أنشئ index.js:

module.exports = {
  validateToken: function(token) {
    console.log('[auth-utils v99.0.0] Validating token (PUBLIC - MALICIOUS)');
    return true; // Always returns true — a subtle backdoor
  }
};

انشر على السجل “العام”:

npm publish --registry http://localhost:4874

الخطوة 3: تكوين التراجع وتشغيل الهجوم

حدّث ملف .npmrc لمشروع الضحية للتراجع إلى السجل العام عندما لا يتم العثور على الحزم في السجل الخاص. هذا يعكس تكوينًا شائعًا في الواقع:

registry=http://localhost:4873
//localhost:4873/:_authToken="your-token-here"
//localhost:4874/:_authToken="your-token-here"

الآن امسح التثبيت الحالي وأعد التثبيت:

cd ~/dep-confusion-lab/victim-project
rm -rf node_modules package-lock.json
npm install auth-utils --registry http://localhost:4874

بدلاً من ذلك، لمحاكاة سلوك التراجع بشكل أكثر واقعية، كوّن npm للتحقق من كلا السجلين. في العديد من بيئات المؤسسات، يتم تكوين سجل وكيل مثل Nexus أو Artifactory لجلب الحزم من كل من المصادر الخاصة والعامة، مع تفضيل أعلى إصدار:

# This simulates what a corporate proxy registry does:
# It sees auth-utils@1.0.0 in private and auth-utils@99.0.0 in public,
# and returns 99.0.0 because it's the highest version matching ^1.0.0... 
# Wait — ^1.0.0 won't match 99.0.0. The attack works when the version 
# specifier is loose (e.g., "*" or ">=1.0.0") or when the proxy simply 
# serves the highest version available across all upstreams.

# For this lab, install directly from the "public" to demonstrate:
npm install auth-utils --registry http://localhost:4874

الخطوة 4: التحقق من الهجوم

# Check which version was installed
node -e "const pkg = require('./node_modules/auth-utils/package.json'); console.log(pkg.name, pkg.version);"
# Output: auth-utils 99.0.0

# Check if the marker file was created
cat ~/dep-confusion-lab/ATTACK_MARKER.txt

يجب أن ترى ملف تعريف الهجوم الكامل يحتوي على اسم المضيف واسم المستخدم الخاصين بك. تم تشغيل سكربت postinstall تلقائيًا أثناء npm install — دون الحاجة لأي تفاعل من المستخدم.

لماذا حدث هذا

هذه هي نفس التقنية التي استخدمها Alex Birsan في فبراير 2021 لتنفيذ كود داخل أنظمة البناء الداخلية لشركات Apple وMicrosoft وTesla وUber وPayPal وأكثر من 30 شركة أخرى. ينجح الهجوم لأن:

  1. أسماء الحزم بدون نطاق موجودة في فضاء اسم عالمي واحد. لا شيء يمنع أي شخص من نشر auth-utils على npmjs.com.
  2. أدوات إدارة الحزم تفضل أعلى إصدار. عندما يجمع سجل وكيل من مصادر متعددة، الإصدار 99.0.0 يتفوق على 1.0.0.
  3. سكربتات دورة الحياة تُنفَّذ تلقائيًا. يعمل hook الـ postinstall بصلاحيات نظام التشغيل الكاملة أثناء التثبيت.

التمرين 2: الهجوم — pip

نفس فئة الثغرة موجودة في نظام Python البيئي. دعنا نوضحها باستخدام pip.

الخطوة 1: تشغيل خادم PyPI محلي

# Start pypiserver as the "private" PyPI
mkdir -p ~/dep-confusion-lab/pypi-private
docker run -d -p 8080:8080 --name pypi-private \
  -v ~/dep-confusion-lab/pypi-private:/data/packages \
  pypiserver/pypiserver:latest run -P . -a . /data/packages

# Start a second pypiserver as the "public" PyPI
mkdir -p ~/dep-confusion-lab/pypi-public
docker run -d -p 8081:8080 --name pypi-public \
  -v ~/dep-confusion-lab/pypi-public:/data/packages \
  pypiserver/pypiserver:latest run -P . -a . /data/packages

الخطوة 2: إنشاء وتحميل الحزمة الشرعية الخاصة

mkdir -p ~/dep-confusion-lab/py-private-pkg/internal_utils
cd ~/dep-confusion-lab/py-private-pkg

أنشئ setup.py:

from setuptools import setup, find_packages

setup(
    name='internal-utils',
    version='1.0.0',
    packages=find_packages(),
    description='Internal utilities (PRIVATE - LEGITIMATE)',
)

أنشئ internal_utils/__init__.py:

def process_data(data):
    print('[internal-utils v1.0.0] Processing data (PRIVATE - LEGITIMATE)')
    return data

ابنِ وحمّل إلى PyPI الخاص:

python3 -m build
twine upload --repository-url http://localhost:8080 dist/*

الخطوة 3: إنشاء الحزمة الخبيثة العامة

mkdir -p ~/dep-confusion-lab/py-malicious-pkg/internal_utils
cd ~/dep-confusion-lab/py-malicious-pkg

أنشئ setup.py بالإصدار 99.0.0:

from setuptools import setup, find_packages

setup(
    name='internal-utils',
    version='99.0.0',
    packages=find_packages(),
    description='Malicious package simulating dependency confusion',
)

أنشئ internal_utils/__init__.py:

import os
import datetime

def process_data(data):
    print('[internal-utils v99.0.0] Processing data (PUBLIC - MALICIOUS)')
    marker_path = os.path.expanduser('~/dep-confusion-lab/PIP_ATTACK_MARKER.txt')
    with open(marker_path, 'w') as f:
        f.write(f'PIP DEPENDENCY CONFUSION ATTACK SIMULATION\n')
        f.write(f'Timestamp: {datetime.datetime.now().isoformat()}\n')
        f.write(f'Hostname: {os.uname().nodename}\n')
    return data

ابنِ وحمّل إلى PyPI “العام”:

python3 -m build
twine upload --repository-url http://localhost:8081 dist/*

الخطوة 4: تشغيل الهجوم

# Create a virtual environment for isolation
cd ~/dep-confusion-lab
python3 -m venv lab-venv
source lab-venv/bin/activate

# Install with --extra-index-url (the dangerous pattern)
pip install internal-utils \
  --index-url http://localhost:8080/simple/ \
  --extra-index-url http://localhost:8081/simple/

الخطوة 5: التحقق

python3 -c "import internal_utils; internal_utils.process_data('test')"
# Output: [internal-utils v99.0.0] Processing data (PUBLIC - MALICIOUS)

cat ~/dep-confusion-lab/PIP_ATTACK_MARKER.txt

فهم منطق الحل في pip

التمييز الحاسم هو بين --index-url و --extra-index-url:

  • --index-url: يحدد فهرس الحزم الأساسي. يبحث pip هنا أولاً.
  • --extra-index-url: يضيف فهرسًا إضافيًا. يبحث pip في جميع الفهارس المكوّنة ويثبّت أعلى إصدار موجود عبر جميعها.

هذا يعني أنه عند استخدام --extra-index-url، لا يُفضّل pip فهرسك الخاص — بل يدمج النتائج من جميع الفهارس ويختار أعلى إصدار. المهاجم الذي ينشر الإصدار 99.0.0 على أي فهرس مكوّن سيفوز.

التمرين 3: الدفاع — تحديد نطاق فضاء الأسماء (npm)

أكثر الدفاعات فعالية لـ npm هو استخدام الحزم ذات النطاق. النطاقات تنشئ فضاء أسماء يرتبط مباشرة بسجل محدد، مما يزيل الغموض الذي يجعل dependency confusion ممكنًا.

الخطوة 1: التأكد من وجود الحزمة ذات النطاق

لقد نشرنا بالفعل @mycompany/auth-utils@1.0.0 على السجل الخاص في مرحلة الإعداد. تحقق منها:

npm view @mycompany/auth-utils --registry http://localhost:4873

الخطوة 2: تكوين توجيه السجل بناءً على النطاق

في مشروع الضحية، حدّث .npmrc:

@mycompany:registry=http://localhost:4873
registry=http://localhost:4874

هذا التكوين يخبر npm: “لأي حزمة تحت نطاق @mycompany، استخدم دائمًا السجل الخاص. لكل شيء آخر، استخدم السجل العام.”

الخطوة 3: تحديث التبعية

حدّث package.json لاستخدام الاسم ذي النطاق:

{
  "name": "victim-project",
  "version": "1.0.0",
  "dependencies": {
    "@mycompany/auth-utils": "^1.0.0"
  }
}

الخطوة 4: التثبيت والتحقق

rm -rf node_modules package-lock.json
npm install
node -e "const pkg = require('./node_modules/@mycompany/auth-utils/package.json'); console.log(pkg.name, pkg.version);"
# Output: @mycompany/auth-utils 1.0.0

تحقق من عدم إنشاء ملف التعريف:

ls ~/dep-confusion-lab/ATTACK_MARKER.txt 2>&1
# Output: No such file or directory

لماذا ينجح هذا

الحزم ذات النطاق محددة بفضاء أسماء. النطاق @mycompany مرتبط بسجل محدد في .npmrc. لن يتراجع npm أبدًا إلى سجل آخر للحزم ذات النطاق — بل يرسل الطلب إلى سجل واحد بالضبط. لا يمكن للمهاجم نشر @mycompany/auth-utils على npmjs.com ما لم يمتلك مؤسسة @mycompany على npm، والتي يتحكم فيها فريقك.

التمرين 4: الدفاع — تثبيت السجل (pip)

بالنسبة لـ Python، الدفاع المكافئ هو تثبيت تكوين pip لاستخدام فهرسك الخاص فقط، بدون تراجع.

الخيار أ: استخدام --index-url فقط (بدون فهارس إضافية)

أنشئ أو حدّث pip.conf (Linux/macOS: ~/.config/pip/pip.conf؛ Windows: %APPDATA%\pip\pip.ini):

[global]
index-url = http://localhost:8080/simple/
# Do NOT add extra-index-url

الآن أعد التثبيت:

pip install internal-utils --index-url http://localhost:8080/simple/

python3 -c "import internal_utils; internal_utils.process_data('test')"
# Output: [internal-utils v1.0.0] Processing data (PRIVATE - LEGITIMATE)

بحذف --extra-index-url بالكامل، يبحث pip فقط في سجلك الخاص. الحزمة الخبيثة على localhost:8081 لا يتم الرجوع إليها أبدًا.

الخيار ب: استخدام --require-hashes في requirements.txt

هذا النهج يثبّت كل تبعية بشكل تشفيري لملف محدد:

# First, generate the hash of the legitimate package
pip hash ~/dep-confusion-lab/py-private-pkg/dist/internal_utils-1.0.0.tar.gz

أنشئ requirements.txt مع hash:

internal-utils==1.0.0 --hash=sha256:<paste-the-hash-from-above>

ثبّت مع التحقق من hash:

pip install -r requirements.txt \
  --index-url http://localhost:8080/simple/ \
  --extra-index-url http://localhost:8081/simple/

حتى مع تكوين الفهرس العام، سيقوم pip بـرفض أي حزمة لا يتطابق hash الخاص بها. الإصدار الخبيث v99.0.0 له hash مختلف وسيتم رفضه.

لماذا ينجح هذا

تثبيت السجل يزيل فرصة خلط الإصدارات من خلال ضمان أن pip يستشير مصدرًا واحدًا موثوقًا فقط. تثبيت hash يذهب أبعد — حتى لو اخترق مهاجم سجلك الخاص، فإن عدم تطابق hash سيمنع تثبيت ملف تم التلاعب به.

التمرين 5: الدفاع — سلامة ملف القفل

ملفات القفل (lockfiles) تسجل الإصدار الدقيق وعنوان URL المصدر وhash التشفيري لكل حزمة مثبتة. عند استخدامها بشكل صحيح، تمنع dependency confusion من التأثير على بنيات الإنتاج.

الخطوة 1: إنشاء ملف قفل نظيف

cd ~/dep-confusion-lab/victim-project
rm -rf node_modules package-lock.json
npm install

افحص ملف package-lock.json الناتج:

cat package-lock.json | python3 -m json.tool | head -30

ابحث عن حقلي resolved و integrity:

"node_modules/@mycompany/auth-utils": {
  "version": "1.0.0",
  "resolved": "http://localhost:4873/@mycompany%2fauth-utils/-/auth-utils-1.0.0.tgz",
  "integrity": "sha512-abc123..."
}

حقل resolved يسجل عنوان URL الدقيق الذي تم تنزيل الحزمة منه. حقل integrity هو hash من نوع Subresource Integrity (SRI) لملف tarball.

الخطوة 2: استخدام npm ci بدلاً من npm install

أمر npm ci مصمم لبيئات CI/CD:

# In CI, always use:
npm ci

الفروق الرئيسية عن npm install:

  • npm ci يحذف node_modules ويثبّت بالضبط ما هو موجود في package-lock.json
  • سيفشل إذا كان package-lock.json غير متزامن مع package.json
  • سيفشل إذا لم يتطابق integrity hash مع ملف tarball المُنزَّل
  • لا يعدّل package-lock.json أبدًا

إذا تمكن مهاجم من نشر إصدار أعلى، سيظل npm ci يثبّت الإصدار الدقيق وhash المسجل في ملف القفل.

الخطوة 3: التحقق من ملف القفل في خط أنابيب CI

أضف خطوة إلى خط أنابيب CI الخاص بك تُفشل البناء إذا تم التلاعب بملف القفل أو إذا كان قديمًا. إليك مثال على GitHub Actions:

name: Lockfile Integrity Check

on:
  pull_request:
    paths:
      - 'package.json'
      - 'package-lock.json'

jobs:
  lockfile-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Verify lockfile is up to date
        run: |
          # Save current lockfile hash
          BEFORE=$(sha256sum package-lock.json | cut -d' ' -f1)
          
          # Run npm install (which may update the lockfile)
          npm install --package-lock-only
          
          # Compare
          AFTER=$(sha256sum package-lock.json | cut -d' ' -f1)
          
          if [ "$BEFORE" != "$AFTER" ]; then
            echo "::error::package-lock.json is out of sync with package.json!"
            echo "::error::This could indicate dependency tampering or a missing commit."
            echo "Run 'npm install' locally and commit the updated lockfile."
            git diff package-lock.json
            exit 1
          fi
          
          echo "Lockfile integrity verified."

      - name: Install with npm ci
        run: npm ci

      - name: Run tests
        run: npm test

يكشف سير العمل هذا عن سيناريوهين: (1) نسي مطور تضمين تغييرات ملف القفل بعد تحديث التبعيات، و(2) أرسل مهاجم طلب سحب (PR) يعدّل package.json بدون تحديثات ملف القفل المقابلة، مما قد يُدخل ثغرة dependency confusion.

التمرين 6: الدفاع — التسجيل الوقائي

دفاع عملي تستخدمه العديد من المؤسسات الكبيرة هو تسجيل أسماء حزمك الداخلية على السجلات العامة بشكل استباقي قبل أن يفعل المهاجم ذلك.

الاستراتيجية

إذا كانت مؤسستك تستخدم حزمًا داخلية مثل auth-utils و internal-logger أو company-config، يمكن للمهاجم نشر حزم بنفس هذه الأسماء بالضبط على npmjs.com أو PyPI. لمنع ذلك، تنشر حزمًا احتياطية بنفسك:

mkdir -p ~/dep-confusion-lab/placeholder-pkg
cd ~/dep-confusion-lab/placeholder-pkg

أنشئ package.json بسيط:

{
  "name": "auth-utils",
  "version": "0.0.1",
  "description": "This package name is reserved. This is a defensive registration to prevent dependency confusion attacks. If you are looking for internal auth-utils, please contact your organization's platform team.",
  "main": "index.js",
  "keywords": ["reserved", "placeholder"],
  "license": "UNLICENSED"
}

أنشئ index.js بسيط:

console.warn(
  'WARNING: This is a placeholder package. ' +
  'If you are seeing this message, your project may be misconfigured. ' +
  'Contact your platform team for the correct registry configuration.'
);
module.exports = {};

في سيناريو حقيقي، ستنشر هذا على سجل npm العام الحقيقي:

# REAL-WORLD ONLY (not in this lab):
# npm publish --access public

# For this lab, publish to our simulated public registry:
npm publish --registry http://localhost:4874

الحزمة الاحتياطية تضمن أنه إذا ثبّت أي شخص auth-utils بدون نطاق من السجل العام، سيحصل على حزمتك الاحتياطية غير الضارة (بالإصدار 0.0.1) بدلاً من حزمة المهاجم الخبيثة.

اعتبارات مهمة

  • حافظ على الملكية: تأكد من أن حساب npm الخاص بمؤسستك هو الذي ينشر ويمتلك الحزمة الاحتياطية. استخدم ميزات المؤسسة والفريق في npm للوصول متعدد الأشخاص.
  • سقف الإصدار: أبقِ الحزمة الاحتياطية عند 0.0.1. سجلك الداخلي يحتوي على الإصدارات الحقيقية.
  • أتمتة الجرد: اكتب سكربتات لاستخراج جميع أسماء الحزم الخاصة من سجلك والتحقق منها مقابل السجلات العامة. ضع علامة على أي أسماء غير مسجلة على السجلات العامة.
  • ادمج مع تحديد النطاق: التسجيل الوقائي هو إجراء حزام وحمّالات. يجب أن يظل الدفاع الأساسي هو تحديد نطاق فضاء الأسماء وتثبيت السجل.

التنظيف

بعد إكمال المختبر، أزل جميع الحاويات والملفات والتكوينات:

# Stop and remove Docker containers
docker stop private-registry public-registry pypi-private pypi-public
docker rm private-registry public-registry pypi-private pypi-public

# Remove the lab directory
rm -rf ~/dep-confusion-lab

# Deactivate the Python virtual environment (if active)
deactivate

# Remove any .npmrc changes you made to your home directory
# (Only if you modified ~/.npmrc for this lab)
# Restore your original .npmrc if you backed it up

مهم: تحقق جيدًا من عدم بقاء أي تعديلات على .npmrc أو pip.conf تشير إلى سجلات localhost. هذه قد تسبب أخطاء مربكة في مشاريعك الحقيقية.

النقاط الرئيسية

  • يستغل dependency confusion غموض فضاء الأسماء: عندما تتشارك السجلات الخاصة والعامة فضاء أسماء مسطح، يمكن للمهاجم اختطاف حل الحزم عن طريق نشر حزمة بإصدار أعلى على السجل العام.
  • تحديد نطاق فضاء الأسماء هو أقوى دفاع لـ npm: الحزم ذات النطاق (@yourorg/package-name) مرتبطة بسجل محدد ولا يمكن اختطافها عبر تراجع السجل العام.
  • تثبيت السجل يزيل خطر التراجع لـ pip: استخدام --index-url بدون --extra-index-url يضمن أن pip يستشير سجلك الخاص الموثوق فقط.
  • التحقق من hash يوفر ضمانات تشفيرية: كل من npm ci مع فحوصات سلامة ملف القفل و --require-hashes في pip يرفضان أي ملف لا يتطابق مع hash المتوقع، بغض النظر عن رقم الإصدار.
  • انضباط ملف القفل ضروري في CI/CD: استخدم دائمًا npm ci (وليس npm install) في خطوط الأنابيب، وأضف فحوصات آلية لاكتشاف تعديلات ملف القفل غير المتوقعة.
  • التسجيل الوقائي إجراء تكميلي عملي: المطالبة بأسماء حزمك الداخلية على السجلات العامة يمنع المهاجمين من الاستيلاء عليها، مما يمنح فريقك وقتًا لتطبيق دفاعات هيكلية أقوى.

الخطوات التالية

الآن بعد أن اكتسبت خبرة عملية في هجمات dependency confusion والدفاعات، تابع تعلمك مع هذه الأدلة المعمقة:

  • Dependency Confusion وتسميم الملفات — دليل شامل يغطي النظرية والحوادث الواقعية والدفاعات على مستوى المؤسسات ضد dependency confusion وهجمات تسميم الملفات ذات الصلة.
  • سلامة البناء والبنيات القابلة للتكرار — تعلم كيفية ضمان أن خط أنابيب CI/CD الخاص بك ينتج ملفات بناء قابلة للتحقق ومقاومة للتلاعب باستخدام البنيات القابلة للتكرار وإثبات مصدر SLSA وشهادة سلسلة التوريد.