أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

SHA Pinning في GitHub Actions: امنع Supply Chain Attack من سرقة CI Secrets

📅 ٢٧ أبريل ٢٠٢٦⏱ 5 دقائق قراءة
SHA Pinning في GitHub Actions: امنع Supply Chain Attack من سرقة CI Secrets

المستوى المطلوب: محترف (Senior DevOps / Platform Engineer)

لو الـ CI/CD pipeline بتاعك بيستخدم actions/checkout@v4 أو uses: tj-actions/changed-files@v45، أنت ضمنيًا بتثق إن صاحب الـ tag مش هيغيّر اللي تحته. الـ tag في GitHub mutable، والافتراض ده اتكسر في حادثة فعلية يوم 14 مارس 2025.

SHA Pinning في GitHub Actions: الحماية الحقيقية لـ Supply Chain

action شائع اسمه tj-actions/changed-files اتخترق، والـ tags كلها (من v1 لـ v45) ابتدت تطبع GITHUB_TOKEN، AWS keys، وnpm tokens في اللوج. أكتر من 23,000 workflow public اتأثروا في أقل من 12 ساعة حسب StepSecurity advisory. الـ workflows اللي كانت بتستخدم commit SHA ثابت بدل tag ما اتأثرتش — لأن الـ SHA بيقفل النسخة عند نقطة معروفة قبل الاختراق.

شاشة كود حمراء تحاكي اختراق سلسلة الإمداد في GitHub Actions وتسريب secrets الـ CI

المشكلة باختصار

السطر uses: owner/action@v3 مش immutable. مالك الـ repo (أو حد اخترق حسابه) يقدر يحرّك الـ tag لأي commit في أي وقت. السطر اللي عندك في الـ workflow ما يتغيّرش بصريًا، لكن الكود اللي بيتنفّذ في الـ runner بيتغيّر فعليًا. النتيجة: arbitrary code execution بصلاحيات الـ GITHUB_TOKEN ووصول كامل لكل secret بتقرأه الخطوة.

تخيّلها بمثال بسيط الأول

تخيّل إنك بتشتري قهوة من نفس الكافيه كل صبح، واللافتة على باب المحل بتقول "كافيه فلان". فجأة المالك بدّل اللافتة على محل تاني، فيه شخص بيدّيك مشروب فيه مادة غريبة. أنت لسه ماشي على نفس اسم اللافتة، لكن داخلك حاجة تانية بالكامل.

الـ tag في GitHub زي اللافتة — اسم بشري قابل للتحريك. الـ commit SHA زي بصمة المحل نفسه: 40 حرف بيعرّفوا نسخة الكود بالظبط، وما يتغيّروش طول ما الـ git history سليم. لو ربطت اللافتة بالبصمة، بقى عندك دليل لو حد لعب.

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

الـ commit SHA-1 في git هو hash بطول 40 hex character (160 bit)، بيتحسب على content الـ commit بالكامل: tree object الجذر + parent SHAs + author + committer + timestamp + message. أي تعديل ولو حرف واحد بيغيّر الـ hash كله بسبب خاصية avalanche في دوال الـ hashing.

GitHub بيقبل تشغيل action عند أي ref: branch, tag, أو commit_sha. الأولين mutable. الأخير immutable طول ما الـ commit موجود في الـ repo. لما تكتب uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab، GitHub بيـ checkout الكود اللي عند الـ commit ده بالظبط، حتى لو الـ tag v4 اتنقل بكره لـ commit مختلف.

الـ workflow الصحيح: pin + comment + automate

  1. اقرأ الـ commit SHA الحالي للـ tag اللي بتستخدمه
  2. استبدل الـ tag بـ SHA كامل، وحط comment فيه الـ version للقراءة البشرية
  3. شغّل Renovate أو Dependabot يقترح SHAs الجديدة بصيغة PR، ما تعملش الترقية يدوي
  4. راجع كل PR ترقية action ولو ثانية واحدة قبل ما تـ merge
YAML
# قبل (mutable, vulnerable)
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: tj-actions/changed-files@v45

# بعد (pinned by SHA + version comment)
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v4.2.2
- uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0
- uses: tj-actions/changed-files@cbda684547adc8c059d5573bcf01b73ad97cd5b9 # v46.0.1

الـ comment بعد الـ SHA مش زينة. أدوات زي Renovate و Dependabot بتقرأه عشان تعرف ترقّيك للـ tag الجديد المقابل لما يطلع release.

أتمتة المهمة بـ Renovate

Pinning يدوي بيموت بعد أسبوعين. لازم automation. ضيف renovate.json ده في جذر الـ repo:

JSON
{
  "extends": [
    "config:base",
    "helpers:pinGitHubActionDigests"
  ],
  "packageRules": [
    {
      "matchManagers": ["github-actions"],
      "pinDigests": true,
      "schedule": ["after 9am on monday"],
      "groupName": "github-actions digests"
    }
  ]
}
بوت أتمتة بيفتح pull request لتحديث SHA digest لـ GitHub Actions أسبوعيًا

النتيجة: كل أسبوع PR واحد بيحدّث الـ SHAs المتوفّرة، الـ comment بيعكس الـ tag الجديد، وانت بتراجع الـ diff بعينك قبل الـ merge. الفكرة هنا مش إن الإنسان يكون أحسن من البوت في كشف الكود الخبيث — الفكرة إن الـ pinning بيفصلك عن الـ window الزمني للهجوم. لما الـ SHA متغيّرش لمدة أسبوع، أي compromise بيحصل في النص بيتم اكتشافه قبل ما يلمسك.

الـ trade-offs اللي لازم تعرفها

المكسب: حماية حقيقية ضد الـ retroactive tag manipulation. الخسارة: تلات تكاليف فعلية، اعرفها قبل ما تتبنى الفكرة على scale.

  • قراءة أصعب: سطر uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab أصعب على المراجع البشري من @v4. الـ comment بيخفّف، مش بيلغي.
  • تأخير في تطبيق patches: لو طلعت ثغرة في v4.2.2 وعملوا v4.2.3، أنت لسه على القديم لحد ما Renovate يفتح PR. مش زي @v4 اللي بياخد الـ patch تلقائيًا.
  • زيادة في PR noise: 12 action × ترقية شهرية ≈ 12 PR إضافي. تعامل معاها بـ batched updates و auto-merge للـ minor/patch فقط بعد ما تـ pass الـ CI.

قياس الأثر بأرقام

الافتراض: organization فيها 80 repo، كل repo بيستخدم 6 actions في المتوسط، يعني 480 reference. حادثة tj-actions أصابت أكتر من 23,000 workflow public على GitHub في أقل من 12 ساعة. الـ orgs اللي كانت pinning by SHA من قبل: 0 ضحايا مؤكدين. التكلفة لو طبّقت Renovate من اليوم الأول: حوالي ساعة setup للـ org كله، + 15 دقيقة مراجعة أسبوعيًا لكل team. الـ ROI واضح لو بتقرأ secrets.* أو بتعمل deploy بـ GITHUB_TOKEN.

متى لا تستخدم SHA Pinning

تلات سيناريوهات Pinning فيها overkill حقيقي:

  • repo شخصي تجريبي مفهوش secrets ولا deploy targets — مفيش حاجة تتسرق
  • action داخلي جوّه نفس المنظمة على private repo، والـ branch protection شغّالة بـ signed commits — الثقة محمية بطبقة تانية
  • workflows بتشتغل على pull_request من forks بـ permissions: read-all فقط، ومش بتقرأ secrets أصلاً

غير كده، لو بتعمل deploy لـ production أو بتقرأ secrets.*: Pinning مش اختيار، ده default سليم.

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

افتح أكبر workflow عندك دلوقتي. شغّل الأمر ده عشان تستخرج SHA الحالي لكل tag:

Bash
gh api repos/actions/checkout/commits/v4 --jq .sha
# 8e5e7e5ab8b370d6c329ec480221332ada57f0ab

استبدل الـ tag بالـ SHA + comment فيه الـ version. بعدين فعّل Renovate config اللي فوق وخلّيه يفتح PRs أسبوعيًا. لو لقيت action مش متاح له release notes واضحة أو فيه maintainer واحد بس، فكّر مرتين قبل ما تستخدمه أصلاً — ده signal أهم من عدد الـ stars.

المصادر

  • StepSecurity Advisory — tj-actions/changed-files compromise (March 14, 2025): stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised
  • GitHub Docs — Security hardening for GitHub Actions § Using third-party actions: docs.github.com/en/actions/security-guides/security-hardening-for-github-actions
  • Renovate Documentation — pinDigests configuration option: docs.renovatebot.com/configuration-options/#pindigests
  • OpenSSF Scorecard — Pinned-Dependencies check: github.com/ossf/scorecard/blob/main/docs/checks.md#pinned-dependencies
  • CISA Alert AA25-074A — Supply Chain Compromise Affecting GitHub Actions (March 2025)

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة