DevSecOps وأمن سلسلة التوريد

فحص الأسرار والاستجابة للتسريبات

18 دقيقة الدرس 9 من 28

فحص الأسرار والاستجابة للتسريبات

مفتاح AWS مضمّن في الكود، أو سر Stripe، أو كلمة مرور قاعدة بيانات ملتزمة في مستودع عام — هذه هي أكثر أنواع حوادث الأمان شيوعاً وأكثرها قابلية للوقاية وأشدها ضرراً في التسليم البرمجي الحديث. حين تبيّن أن اختراق Uber عام 2022 يعود إلى بيانات اعتماد مخزّنة في مستودع GitHub خاص، وحين تقوم روبوتات آلية بفحص مستودعات GitHub خلال ثوانٍ من كل push، فإن السؤال ليس هل تفحص الأسرار — بل كم طبقة دفاع تشغّل.

يغطي هذا الدرس دورة الحياة الكاملة: حجب الأسرار قبل دخولها إلى نظام التحكم بالإصدارات، واصطياد ما يتسرب منها في CI، والتنبيه على التسريبات في التاريخ الموجود، وتنفيذ دليل الاستجابة للحوادث حين يخرج مفتاح ما إلى العالم الخارجي.

الطبقة الأولى: خطافات ما قبل الالتزام مع detect-secrets و Gitleaks

أرخص وقت لاكتشاف السر هو قبل إنشاء الالتزام. Gitleaks وdetect-secrets هما الأداتان الأكثر انتشاراً، وتعمل معظم الفرق الناضجة بالأداتين معاً — Gitleaks لسرعته وتغطيته الواسعة بالتعابير المنتظمة، وdetect-secrets لكشفه القائم على الإنتروبيا وسير عمل تدقيق الخط الأساسي.

الإعداد القياسي يستخدم pre-commit (إطار عمل Python) لتنسيق الخطافات من ملف .pre-commit-config.yaml واحد في جذر المستودع:

# .pre-commit-config.yaml (يُودَع في كل مستودع) repos: - repo: https://github.com/gitleaks/gitleaks rev: v8.18.4 hooks: - id: gitleaks # يفشل الالتزام إذا تطابق أي نمط سري # الإعداد من .gitleaks.toml في جذر المستودع - repo: https://github.com/Yelp/detect-secrets rev: v1.5.0 hooks: - id: detect-secrets args: ['--baseline', '.secrets.baseline'] # التشغيل الأول: detect-secrets scan > .secrets.baseline # ودّع الخط الأساسي؛ فقط الأسرار الجديدة الصافية تفشل الالتزامات المستقبلية

ثبّت وفعّل بـ pip install pre-commit && pre-commit install. على أجهزة Mac يمكن التوزيع عبر Homebrew. للمستودعات التي لا تستطيع ضمان تشغيل كل مساهم لـ pre-commit install، الخطاف عائق بسيط لا ضمان — لهذا فحص CI إلزامي.

يقرأ Gitleaks إعداد .gitleaks.toml لقوائم السماح والقواعد المخصصة. أهم ضبط دقيق هو قائمة سماح خاصة بالمشروع حتى لا تولّد تركيبات الاختبار ببيانات اعتماد مزيفة ضجيجاً:

# .gitleaks.toml [allowlist] description = "تركيبات اختبار مقصودة وأمثلة توثيق" regexes = [ '''EXAMPLE''', '''AKIAIOSFODNN7EXAMPLE''', '''my-placeholder-secret''', ] paths = [ '''tests/fixtures/.*''', '''docs/.*''', ] [[rules]] id = "internal-api-token" description = "نمط رمز النشر الداخلي" regex = '''deploy_token_[a-z0-9]{32}''' tags = ["secret", "internal"] severity = "CRITICAL"
الخط الأساسي مقابل الحجب: في مستودع جديد، شغّل Gitleaks في وضع الحجب منذ اليوم الأول. في مستودع بتاريخ موجود، ولّد خطاً أساسياً لـ detect-secrets أولاً (detect-secrets scan > .secrets.baseline) حتى لا تحجب المشكلات الموجودة في الخط الأساسي جميع الالتزامات — فقط الأسرار الجديدة الصافية تثير الفشل. جدوِل مهمة تدقيق منفصلة لمعالجة متأخرات الخط الأساسي.

الطبقة الثانية: الفحص في خط CI

CI هو بوابة التطبيق الإلزامية. حتى لو نفّذ كل مطوّر pre-commit بإخلاص، يصطاد فحص CI عمليات force-push، والالتزامات عبر محرر الويب، والتزامات روبوتات GitHub Actions التي تتجاوز الخطافات المحلية. شغّل Gitleaks على الفرق الكاملة لكل طلب سحب، وعلى كل push للفروع المحمية. العلامة --source تفحص فقط الالتزامات المدخلة في طلب السحب، مما يبقي وقت الفحص تحت خمس ثوانٍ لمعظم المستودعات.

# .github/workflows/secret-scan.yml name: Secret Scan on: push: branches: ["main", "master", "release/**"] pull_request: jobs: gitleaks: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # التاريخ الكامل مطلوب لفحص السجل - name: Run Gitleaks (PR diff only) uses: gitleaks/gitleaks-action@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} # للمستودعات التنظيمية with: args: --source . --log-opts="origin/main..HEAD" --report-format sarif --report-path gitleaks.sarif - name: Upload SARIF to GitHub Security if: always() uses: github/codeql-action/upload-sarif@v3 with: sarif_file: gitleaks.sarif

ميزة Secret Scanning الخاصة بـ GitHub (متاحة في جميع المستودعات العامة وGitHub Advanced Security) تعمل باستمرار في الخلفية وترسل تنبيهات حماية push قبل وصول الالتزام إلى الفرع الافتراضي. فعّلها من Settings → Code security → Secret scanning → Push protection. تغطي أكثر من 200 نمط رمز شريك (AWS وGCP وStripe وTwilio وSlack وغيرها) بمعدلات إيجابيات كاذبة شبه معدومة لأن الأنماط تُتحقق منها في واجهة برمجة التطبيقات المباشرة للمزوّد.

الطبقة الثالثة: فحص التاريخ الموجود

حين تستقبل مستودعاً موجوداً، يجب فحص تاريخ الالتزامات الكامل — ليس فقط HEAD الحالي. الأسرار المحذوفة في التزام لاحق لا تزال في مخزن كائنات git، مرئية لأي شخص يستنسخ المستودع. استخدم gitleaks detect --source . --log-opts="--all" أو truffleHog لفحوصات التاريخ العميق. هذه مهمة تدقيق لمرة واحدة؛ نتائجها تُغذّي متأخرات المعالجة المتتبعة في نظام الأمان (Jira أو GitHub Issues أو لوحة إدارة أسرار مخصصة).

Secrets Scanning Defense-in-Depth Layers Dev Machine pre-commit hook Gitleaks (local) detect-secrets baseline يحجب الالتزام git push CI Pipeline Gitleaks (PR diff) SARIF → GH Security Push Protection (GitHub native) يحجب الدمج merged Continuous History scan GH Secret Scanning (always-on) truffleHog periodic audit ينبّه فريق الأمان تسرب! Incident Response 1. Revoke 2. Rotate 3. Rewrite 4. Audit 5. Root cause
الدفاع متعدد الطبقات: ثلاث طبقات فحص بالإضافة إلى دليل الاستجابة للحوادث تضمن اكتشاف السر المتسرب واحتواءه في أبكر مرحلة ممكنة.

الاستجابة للحوادث: دليل الخطوات الخمس

حين يتسرب سر — يكتشفه ماسح، أو يبلّغ عنه باحث bug bounty، أو يُكتشف في إشعار اختراق — السرعة هي كل شيء. قِيست الروبوتات وهي تستهلك مفاتيح AWS المدفوعة حديثاً خلال أربع ثوانٍ من التعرض. كل دقيقة تأخر تتوسع فيها دائرة الأضرار.

  1. الإلغاء الفوري. لا تنتظر تأكيد الاستغلال. اذهب إلى وحدة تحكم مزوّد بيانات الاعتماد (AWS IAM، أو GCP IAM، أو لوحة تحكم Stripe، أو إعدادات GitHub) وعطّل المفتاح أو احذفه. إن كان المفتاح يتحكم في البنية التحتية، اقبل الانقطاع المؤقت — فهو أقل ضرراً من الاختراق. لمفاتيح AWS: aws iam delete-access-key --access-key-id AKIA.... لرموز GitHub: ألغِها من Settings → Developer settings → Personal access tokens.
  2. التدوير وإصدار بديل. أنشئ بيانات اعتماد جديدة فوراً وأدخلها في مدير الأسرار (HashiCorp Vault، أو AWS Secrets Manager، أو GCP Secret Manager). حدّث جميع الأنظمة المستخدمة للمفتاح القديم. لا تُعد استخدام مواد الاعتماد المخترقة أبداً.
  3. إعادة كتابة تاريخ git (أو قبول التعرض). إزالة سر من الالتزام الحالي لا تزيله من تاريخ git — كل من استنسخ المستودع أو خزّنه تخبئةً يملكه بالفعل. استخدم BFG Repo-Cleaner أو git filter-repo لإعادة كتابة التاريخ، ثم force-push وأخطر جميع المتعاونين بإعادة الاستنساخ. هذا مُعطّل على المستودعات النشطة؛ وازنه مع ملف مخاطر بيانات الاعتماد المسرّبة.
  4. تدقيق سجلات الوصول. اسحب سجلات CloudTrail (AWS)، أو سجلات تدقيق Stackdriver (GCP)، أو ما يعادلها. ابحث عن استدعاءات API بالمفتاح المخترق من IPs غير متوقعة، أو مناطق غير معتادة، أو في أوقات غير اعتيادية. هذا يحدد ما إذا كان الاستغلال قد وقع وما الموارد التي جرى الوصول إليها.
  5. السبب الجذري والإصلاح الشامل. اكتب تقرير ما بعد الحادث بلا لوم: كيف وصل السر إلى الالتزام؟ هل كان خطاف pre-commit مفقوداً؟ هل كان مطوّر يتجاوز الخطافات بـ --no-verify؟ هل جرى تسريب سر CI عرضاً في السجلات؟ عالج الثغرة الشاملة — ليس هذه الحالة فحسب.
لا تستخدم git commit --amend أو soft-reset أبداً لـ"إزالة" سر. يظل كائن الالتزام الأصلي في مخزن كائنات git ويمكن الوصول إليه من أي شخص لديه استنساخ أو ذاكرة تخزين مؤقت على GitHub حتى تُجرى إعادة كتابة تاريخ كاملة. افترض أن السر متاح للعموم تماماً لحظة دفعه.

منع الأسرار من المصدر: تكامل Vault

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

في بيئة Kubernetes، النمط القياسي هو External Secrets Operator (ESO)، الذي يزامن الأسرار من Vault أو AWS Secrets Manager إلى كائنات Kubernetes Secret التي تستخدمها الحاوية كمتغيرات بيئة أو ملفات مُركّبة. مواد السر لا تمر أبداً عبر خط CI أو بناء صورة الحاوية.

# مانيفست ExternalSecret — يزامن من AWS Secrets Manager إلى K8s Secret apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: name: myapp-db-credentials namespace: production spec: refreshInterval: 1h secretStoreRef: name: aws-secretsmanager kind: ClusterSecretStore target: name: myapp-db-credentials # اسم K8s Secret الذي يُنشئه/يحدّثه ESO creationPolicy: Owner data: - secretKey: DB_PASSWORD # المفتاح في K8s Secret remoteRef: key: production/myapp/db # المسار في AWS Secrets Manager property: password # مفتاح JSON داخل السر
كشف الأسرار المبكر في IDEs: ثبّت امتداد Gitleaks لـ VS Code أو مكوّن GitGuardian للـ IDE. يرى المطوّرون اكتشافات الأسرار مباشرةً أثناء الكتابة، قبل أي محاولة التزام. بالتوازي مع خطافات pre-commit وفحص CI، يُنشئ هذا دفاعاً من ثلاث طبقات ما قبل الإنتاج يطابق طريقة عمل Google وMeta في حوكمة الأسرار الداخلية.

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