تأمين GitHub Actions
تأمين GitHub Actions
يمتلك pipeline الـ CI/CD وصولاً مميزاً إلى قاعدة الكود الخاصة بك، وأسرارك، وبنيتك التحتية الإنتاجية. Pipeline غير آمن هو سطح هجوم مباشر. حوادث سلسلة التوريد البارزة — SolarWinds، واختراق Codecov، واختراق tj-actions/changed-files عام 2023 — كلها استغلت أنظمة CI/CD لسرقة الأسرار أو دفع كود خبيث. تعامل فرق الأمان في شركات التقنية الكبرى pipeline كحدود ثقة تُحرَس بعناية مثل محيط شبكة الإنتاج. يُغطي هذا الدرس أربعة ضوابط مترابطة: تثبيت الـ actions على SHAs محددة للكوميت، وتضييق صلاحيات GITHUB_TOKEN، وفهم ومنع هجمات pwn-request، وإدارة الـ actions المسموح بها في منظمتك.
تثبيت الـ Actions على SHAs للكوميت
كل سطر uses: owner/action@ref في workflow الخاص بك هو تبعية خارجية. حين تكتب uses: actions/checkout@v4، تحل GitHub العلامة v4 إلى الكوميت الذي تشير إليه حالياً وتُشغّل ذلك الكود مع وصول كامل للـ runner — بما في ذلك أسرارك. العلامات القابلة للتغيير هي المشكلة: يمكن للمُشغّل (أو مهاجم اخترق المُشغّل) تحريك العلامة إلى كوميت جديد بصمت. سيبدأ workflow الخاص بك بتشغيل كود مختلف في تشغيله التالي دون أي إشارة في ملف الـ workflow الخاص بك.
الحل هو تثبيت كل action على SHA كوميت كامل الطول وغير قابل للتغيير:
لإيجاد SHA لعلامة: افتح مستودع الـ action على GitHub، انتقل إلى الإصدار أو العلامة، وانسخ SHA الكوميت الكامل المكوّن من 40 حرفاً من URL أو قائمة كوميتات الإصدار. بدلاً من ذلك، استخدم الـ gh CLI:
.github/dependabot.yml مع package-ecosystem: github-actions) تُؤتمت هذا — تفتح PRs لتحديث SHAs المثبّتة حين تُطلق إصدارات جديدة من الـ actions، فتحصل على أمان التثبيت دون تكلفة التتبع اليدوي.صلاحيات GITHUB_TOKEN: مبدأ أقل الامتيازات
يُزوَّد كل تشغيل workflow تلقائياً بـ GITHUB_TOKEN قصير الأجل. يمكن لهذا الـ token القراءة والكتابة في مستودعك — إنشاء issues، دفع commits، نشر حزم، الكتابة في GitHub Container Registry، والمزيد. الصلاحيات الافتراضية تعتمد على إعدادات المنظمة، لكن الافتراضي القديم لـ GitHub يمنح وصول كتابة للمحتويات. هذا أوسع بكثير مما تحتاجه وظيفة اختبار.
الضابط هو مفتاح permissions، الذي يمكن تعيينه على مستوى الـ workflow (يُطبَّق على جميع الوظائف) ويُتجاوز على مستوى الوظيفة (يُطبَّق على تلك الوظيفة فقط). ثبّت دائماً أضيق الصلاحيات الممكنة:
permissions: write-all أبداً: من المغري تعيين permissions: write-all لـ "جعله يعمل فقط" حين تفشل خطوة بسبب خطأ في الصلاحيات. هذا يمنح token الـ workflow وصول كتابة لكل نطاق API تعرضه GitHub — issues وpull-requests وpackages وdeployments وchecks وcode scanning والمزيد. يمكن عندها لـ action مخترقة في سلسلة تبعياتك (حتى تبعية غير مباشرة عبر reusable workflow) إنشاء إصدارات مزيفة، أو الموافقة على PRs خاصة بها، أو استخراج الأسرار عبر استدعاءات GitHub API. دائماً تحقق من النطاق المحدد الذي تحتاجه خطوة ما ومنح ذلك فقط.هجوم pwn-Request: الخطر الأكثر استهانة به
هجوم pwn-request (اختصار "owned pull request") هو أحد أخطر فئات الثغرات في GitHub Actions، ولا تعلم به كثير من الفرق. فهمه أمر جوهري.
الهجوم يستغل الفرق بين حدثي تشغيل:
pull_request— يُطلَق حين يُفتح PR من fork. بحكم التصميم، يُشغّل هذا الحدث كود workflow الفرع الأساسي، والـGITHUB_TOKENالذي يوفره يمتلك صلاحيات قراءة فقط ولا وصول للأسرار. هذا آمن.pull_request_target— يُطلَق في سياق المستودع الأساسي، لا الـ fork. يمكن للـ workflow الوصول إلى الأسرار والـGITHUB_TOKENيمتلك صلاحيات كتابة. أُنشئ هذا الحدث للسماح بأشياء كتصنيف PRs تلقائياً أو نشر تعليقات من forks، لكنه بالغ الخطورة إذا استنسخت كود الـ PR وشغّلته.
نمط الهجوم: يفتح مساهم خبيث PR من fork. إن كان workflow الخاص بك يستخدم pull_request_target ويستنسخ أيضاً كود الـ PR (باستخدام github.event.pull_request.head.sha)، يعمل كود المهاجم بوصول كتابة كامل لمستودعك ووصول كامل لأسرارك. في 2023، اخترقت هذه الفئة من الثغرات مشاريع مفتوحة المصدر كبرى، مُسرِّبةً مفاتيح التوقيع وبيانات اعتماد النشر.
pull_request_target: بعض حالات الاستخدام الحقيقية تتطلبه — مثلاً التصنيف التلقائي لـ PR من fork بناءً على المسارات المُغيَّرة، أو نشر تعليق تغطية الاختبارات. النمط الآمن هو workflow مقسوم: شغّل CI مع pull_request (بلا أسرار، كود غير موثوق)، واستخدم pull_request_target فقط لوظيفة منفصلة لا تستنسخ كود الـ PR أبداً. مرّر البيانات بينهما عبر artifacts وحدث workflow_run. هذا النمط الذي تستخدمه مشاريع مثل CPython وTypeScript.سياسة الـ Actions المسموح بها: الحوكمة على مستوى المنظمة
على مستوى المنظمة أو المؤسسة، تسمح GitHub للمسؤولين بتقييد أي actions يمكن استخدامها عبر جميع المستودعات. هذه هي طبقة السياسة التي تمنع مهندساً من إدخال action خارجية غير مُدقَّقة عن طريق الخطأ في pipeline حرجة.
الخيارات (مُهيَّأة في واجهة GitHub تحت Organization → Settings → Actions → General) هي:
- السماح بجميع الـ actions — يمكن استخدام أي action من أي مستودع عام. هذا الافتراضي ومناسب فقط للمستودعات الشخصية أو المشاريع منخفضة المخاطر.
- السماح بـ actions محددة — أهم إعداد للمنظمات الإنتاجية. يمكنك تحديد: السماح بـ actions من GitHub نفسها (
actions/*)، والسماح بـ actions من المنشئين الموثّقين (قائمة مُنتقاة تحتفظ بها GitHub)، و/أو السماح بـ actions خارجية محددة بـowner/repo@SHAدقيقاً أو نمط glob. هذا ما تستخدمه Google وStripe وشركات مماثلة لمنظماتها الداخلية على GitHub. - تعطيل GitHub Actions — الخيار النووي؛ يعطّل منصة Actions بأكملها للمنظمة.
بعيداً عن سياسة واجهة المستخدم، يمكنك فرض تثبيت الـ action وممارسات الأمان على مستوى الـ workflow باستخدام Zizmor أو actionlint في CI — أدوات تحليل ثابت تكتشف الـ actions غير المثبّتة، والأنماط الخطرة مثل pull_request_target مع checkout، وكتل الصلاحيات المفقودة:
حقن الأوامر عبر تعبيرات الـ Workflow
نمط أمني حرج آخر يجب أن يعرفه كل مهندس: البيانات غير الموثوقة في تعبيرات الـ workflow. حين تُدرج متغير سياق GitHub مثل ${{ github.event.pull_request.title }} مباشرة في أمر shell لـ run:، أنت عرضة للحقن. يصنع المهاجم عنواناً للـ PR يحتوي على محارف خاصة بالـ shell أو أوامر، وتعمل تلك الأوامر في runner الخاص بك بكل ما تمتلكه الوظيفة من صلاحيات.
github.head_ref في أمر shell. القاعدة بسيطة — لا تُدرِج أي قيمة سياق يتحكم فيها المستخدم (github.event.*، github.head_ref، عناوين الـ PR، رسائل الكوميت، نص الـ issue) مباشرة في كتلة run:. مرّر دائماً عبر متغير بيئة.الأمان في GitHub Actions ليس قائمة مراجعة تُشغّلها مرة واحدة — بل انضباط مستمر. ثبّت actions الخاصة بك وأتمت التحديثات مع Dependabot. عيّن permissions: contents: read على مستوى الـ workflow ومنح وصول الكتابة فقط حيث تحتاجه وظيفة محددة. لا تستخدم pull_request_target أبداً لتشغيل كود fork غير موثوق. فعّل سياسة allowed-actions على مستوى المنظمة. شغّل actionlint في CI. ومرّر دائماً البيانات التي يتحكم فيها المستخدم عبر متغيرات البيئة، لا عبر إدراج التعبيرات في الـ shell. طبّق هذه الضوابط الخمسة وستكون لديك وضعية pipeline تُطابق ما تتطلبه المنظمات الهندسية الواعية بالأمان.