مقدمة: لماذا تُعد الأسرار السبب الأول لاختراق CI/CD
إذا فحصت السبب الجذري لكل اختراق رئيسي تقريباً لخطوط أنابيب CI/CD في السنوات الأخيرة — من هجوم سلسلة التوريد على Codecov إلى حادثة الأمان في CircleCI — ستجد نفس الجاني: الأسرار المكشوفة. مفاتيح API، وبيانات اعتماد السحابة، وكلمات مرور قواعد البيانات، وشهادات التوقيع — هذه هي المفاتيح الرئيسية التي يسعى المهاجمون للحصول عليها، وخطوط أنابيب CI/CD هي المكان الذي يركزون فيه جهودهم.
السبب هيكلي. فخطوط الأنابيب تقع في موقع خطير بشكل فريد: يجب أن يكون لديها وصول إلى بيانات اعتماد الإنتاج لنشر البرمجيات، ومع ذلك فهي بطبيعتها مؤقتة ومتعددة المستأجرين ومعرضة لكود غير موثوق. كل طلب سحب، وكل تحديث للتبعيات، وكل دفع من المساهمين يُفعّل تنفيذ خط الأنابيب — وكل تشغيل هو ناقل محتمل لتسريب الأسرار.
التحدي ليس ببساطة “لا تضع الأسرار في الكود.” بل هو أعمق من ذلك بكثير. كيف تمنح بيئة حوسبة قصيرة العمر وقابلة للتخلص منها وصولاً إلى أكثر بيانات اعتمادك حساسية دون أن تتسرب تلك البيانات إلى السجلات أو المخرجات أو المهام اللاحقة أو أيدي الجهات الخبيثة؟ هذا هو السؤال الذي يجيب عليه هذا الدليل.
سنتناول كيفية تعرض الأسرار للكشف، وكيفية حقنها بأمان، وكيفية دمج HashiCorp Vault واتحاد الهوية السحابية الأصلية، وما هي الأنماط المضادة التي يجب تجنبها. هذا دليل عملي — توقع ملفات YAML حقيقية وأوامر CLI حقيقية وقرارات معمارية حقيقية.
كيف تتعرض الأسرار للكشف في CI/CD
قبل أن نناقش الحلول، نحتاج إلى فهم مشهد التهديدات. تتسرب الأسرار من خطوط الأنابيب عبر عدة نواقل موثقة جيداً.
الأسرار المضمنة في ملفات إعداد خطوط الأنابيب و IaC
أبسط ناقل تسريب — والذي لا يزال شائعاً بشكل مقلق — هو بيانات الاعتماد المضمنة مباشرة في ملفات إعداد خطوط الأنابيب أو قوالب Infrastructure as Code. قد يقوم مطور يختبر عملية نشر بوضع مفتاح وصول AWS في ملف .github/workflows/deploy.yml أو ملف Terraform main.tf، ثم يقوم بعمل commit وينسى الأمر. حتى لو تمت إزالته في commit لاحق، يبقى السر موجوداً إلى الأبد في سجل Git.
# NEVER DO THIS — hardcoded credentials in a workflow file
jobs:
deploy:
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
steps:
- run: aws s3 sync ./build s3://my-bucket
الأسرار في متغيرات البيئة المطبوعة في السجلات
تقوم منصات CI عادةً بحقن الأسرار كمتغيرات بيئة. تنشأ المشكلة عندما تقوم خطوات خط الأنابيب بطباعة تلك المتغيرات إلى stdout عن غير قصد. أمر env غير مدروس، أو printenv للتصحيح، أو أداة مطولة تعرض إعداداتها يمكن أن تكشف الأسرار في سجلات البناء التي غالباً ما يتم الاحتفاظ بها لأيام أو أسابيع ويمكن لجميع أعضاء المشروع الوصول إليها.
# Dangerous: this prints ALL environment variables, including secrets
- run: printenv | sort
# Also dangerous: verbose flags in tools that dump config
- run: terraform plan -debug
الأسرار المحفوظة في مخرجات البناء أو طبقات الحاويات
قد يظل السر المحقون أثناء بناء Docker موجوداً في طبقة وسيطة حتى بعد حذفه في تعليمة RUN لاحقة. وبالمثل، قد تحتوي مخرجات البناء — ملفات JAR وZIP والملفات التنفيذية المجمعة — على ملفات إعداد تتضمن بيانات اعتماد كانت موجودة وقت البناء.
# BAD: The secret persists in the layer created by the COPY instruction
COPY .env /app/.env
RUN /app/setup.sh
RUN rm /app/.env # Too late — it is still in a previous layer
الأسرار المتاحة لسير عمل طلبات السحب غير الموثوقة
هذا أحد أخطر النواقل، خاصة في مشاريع المصدر المفتوح. لا يوفر GitHub Actions، على سبيل المثال، الأسرار لسير العمل التي يتم تشغيلها بواسطة pull_request من النسخ المتشعبة — وذلك حسب التصميم. ومع ذلك، فإن حدث pull_request_target لديه وصول إلى الأسرار، وإذا قام سير العمل بسحب وتنفيذ كود مؤلف طلب السحب، فإنه ينشئ مساراً مباشراً لتسريب الأسرار.
نطاقات الأسرار الواسعة بشكل مفرط
تقوم العديد من المؤسسات بتكوين الأسرار على مستوى المؤسسة أو المجموعة بينما يجب أن تكون محصورة في المستودعات أو البيئات الفردية. السر على مستوى المؤسسة في GitHub Actions متاح لـ كل مستودع في تلك المؤسسة. إذا تم اختراق أي من تلك المستودعات — أو كان يحتوي ببساطة على سير عمل مُعدّ بشكل خاطئ — فإن جميع الأسرار على مستوى المؤسسة معرضة للخطر.
أنماط حقن الأسرار
الآن بعد أن فهمنا كيف تتسرب الأسرار، دعونا نفحص كيفية إدخالها في خطوط الأنابيب بأمان.
أسرار المنصة الأصلية
توفر كل منصة CI/CD رئيسية آلية مدمجة لإدارة الأسرار. يحتوي GitHub Actions على أسرار المستودع والبيئة والمؤسسة. يحتوي GitLab CI على متغيرات CI/CD على مستوى المشروع والمجموعة مع إخفاء وحماية اختياريين. هذه هي أبسط نقطة بداية.
# GitHub Actions: referencing a repository secret
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to production
env:
API_KEY: ${{ secrets.PRODUCTION_API_KEY }}
run: ./deploy.sh
# GitLab CI: using a masked, protected variable
deploy:
stage: deploy
script:
- echo "Deploying with masked credentials"
- ./deploy.sh
variables:
API_KEY: $PRODUCTION_API_KEY
only:
- main
تعد أسرار المنصة الأصلية كافية للعديد من حالات الاستخدام، لكنها تعاني من قيود كبيرة: لا توليد ديناميكي، وتسجيل تدقيق محدود، وتدوير يدوي، وعدم وجود إدارة مركزية عبر منصات متعددة.
مديرو الأسرار الخارجيون
بالنسبة للمؤسسات ذات متطلبات الأمان الناضجة، يوفر مديرو الأسرار الخارجيون — HashiCorp Vault وAWS Secrets Manager وGCP Secret Manager وAzure Key Vault — تحكماً مركزياً وتسجيل تدقيق وتوليد أسرار ديناميكي وتدويراً تلقائياً وسياسات وصول دقيقة. سنتعمق في تكامل Vault في القسم التالي.
الحقن الفوري مقابل الأسرار المحملة مسبقاً
يتم تكوين الأسرار المحملة مسبقاً مرة واحدة وإتاحتها لجميع عمليات تشغيل خط الأنابيب. هذه هي الطريقة التي تعمل بها معظم أسرار المنصة الأصلية. يسترجع الحقن الفوري (JIT) الأسرار في اللحظة التي تكون فيها مطلوبة، غالباً مع فترات صلاحية قصيرة (TTL). الحقن الفوري أفضل لأنه يقلل نافذة التعرض، ويمكّن من استخدام بيانات اعتماد ديناميكية، ويوفر مسارات تدقيق لكل تشغيل.
# JIT injection: fetch the secret only when needed
- name: Get database credentials
run: |
DB_CREDS=$(vault kv get -format=json secret/data/myapp/db)
export DB_USER=$(echo $DB_CREDS | jq -r '.data.data.username')
export DB_PASS=$(echo $DB_CREDS | jq -r '.data.data.password')
./run-migrations.sh
الأسرار المُخفاة مقابل المشفرة
هناك مفهوم خاطئ شائع: “مُخفى” لا يعني “آمن.” عندما يُخفي GitHub Actions سراً، فإنه يقوم باستبدال النص في مخرجات السجل. إذا كانت قيمة السر قصيرة (مثل رمز من 4 أحرف)، فقد لا يتم تفعيل الإخفاء. إذا كان السر مشفراً بـ base64 أو تم تحويله بأي طريقة، فإن القيمة المحولة لن يتم إخفاؤها. الإخفاء هو تسهيل وليس حداً أمنياً. الأسرار المشفرة أثناء التخزين (التي توفرها جميع المنصات الرئيسية) تحمي من اختراق التخزين على جانب المنصة لكنها لا تفعل شيئاً لمنع التسريب أثناء التشغيل.
دمج HashiCorp Vault مع CI/CD
يُعد HashiCorp Vault أكثر مديري الأسرار الخارجيين اعتماداً لخطوط أنابيب CI/CD. يدعم طرق مصادقة متعددة مناسبة للأنظمة المؤتمتة، وتوليد الأسرار الديناميكية، والسياسات الدقيقة. إليك كيفية دمجه مع أكثر منصتي CI/CD شيوعاً.
مصادقة Vault AppRole لعُقد CI
AppRole هي طريقة المصادقة الموجهة للآلات في Vault. تستخدم Role ID (مثل اسم المستخدم) وSecret ID (مثل كلمة المرور) للمصادقة. يمكن تكوين Secret ID للاستخدام لمرة واحدة مع فترة صلاحية محددة (TTL)، مما يجعله مناسباً لعُقد CI.
# Enable AppRole auth method
vault auth enable approle
# Create a policy for CI
vault policy write ci-deploy - <<EOF
path "secret/data/myapp/*" {
capabilities = ["read"]
}
path "database/creds/myapp-role" {
capabilities = ["read"]
}
EOF
# Create an AppRole with the CI policy
vault write auth/approle/role/ci-deploy \
token_policies="ci-deploy" \
token_ttl=15m \
token_max_ttl=30m \
secret_id_ttl=10m \
secret_id_num_uses=1
# Retrieve the Role ID (store in CI platform as a non-sensitive variable)
vault read auth/approle/role/ci-deploy/role-id
# Generate a single-use Secret ID (store in CI platform as a secret)
vault write -f auth/approle/role/ci-deploy/secret-id
مصادقة Vault JWT/OIDC مع GitHub Actions
النهج الحديث والمفضل لـ GitHub Actions هو مصادقة JWT/OIDC. يمكن لـ GitHub Actions إصدار رمز OIDC لكل تشغيل لسير العمل، ويمكن لـ Vault التحقق من صحة هذا الرمز لمصادقة خط الأنابيب — مما يلغي الحاجة إلى تخزين أي بيانات اعتماد Vault في GitHub.
# Configure Vault JWT auth for GitHub Actions
vault auth enable jwt
vault write auth/jwt/config \
bound_issuer="https://token.actions.githubusercontent.com" \
oidc_discovery_url="https://token.actions.githubusercontent.com"
# Create a role that binds to a specific repo and branch
vault write auth/jwt/role/github-deploy \
role_type="jwt" \
bound_audiences="https://github.com/my-org" \
bound_claims_type="glob" \
bound_claims='{"sub": "repo:my-org/my-repo:ref:refs/heads/main"}' \
user_claim="repository_owner" \
token_policies="ci-deploy" \
token_ttl="10m"
ثم في سير عمل GitHub Actions الخاص بك، استخدم hashicorp/vault-action:
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- name: Import secrets from Vault
uses: hashicorp/vault-action@v3
with:
url: https://vault.mycompany.com
method: jwt
role: github-deploy
jwtGithubAudience: https://github.com/my-org
secrets: |
secret/data/myapp/db username | DB_USER ;
secret/data/myapp/db password | DB_PASS
- name: Run deployment
run: |
echo "Deploying with fetched credentials"
./deploy.sh
مصادقة Vault JWT مع GitLab CI
يتمتع GitLab CI بدعم أصلي لتكامل Vault باستخدام id_tokens. يمكن لـ GitLab إنشاء رمز JWT يتحقق منه Vault، على غرار نهج GitHub Actions.
# Configure Vault for GitLab JWT auth
vault auth enable -path=gitlab jwt
vault write auth/gitlab/config \
bound_issuer="https://gitlab.com" \
jwks_url="https://gitlab.com/-/jwks" \
supported_algs="RS256"
vault write auth/gitlab/role/gitlab-deploy \
role_type="jwt" \
bound_claims='{"project_id": "12345", "ref_protected": "true"}' \
user_claim="user_email" \
token_policies="ci-deploy" \
token_ttl="10m"
وفي ملف .gitlab-ci.yml الخاص بك:
deploy:
stage: deploy
id_tokens:
VAULT_ID_TOKEN:
aud: https://vault.mycompany.com
secrets:
DB_USER:
vault: myapp/db/username@secret
token: $VAULT_ID_TOKEN
DB_PASS:
vault: myapp/db/password@secret
token: $VAULT_ID_TOKEN
script:
- ./deploy.sh
الأسرار الديناميكية
إحدى أقوى ميزات Vault هي توليد الأسرار الديناميكية. بدلاً من تخزين كلمات مرور قواعد البيانات الثابتة، يمكن لـ Vault إنشاء بيانات اعتماد قصيرة العمر حسب الطلب. عندما ينتهي خط الأنابيب، تنتهي صلاحية بيانات الاعتماد تلقائياً.
# Enable the database secrets engine
vault secrets enable database
# Configure a PostgreSQL connection
vault write database/config/myapp-db \
plugin_name=postgresql-database-plugin \
connection_url="postgresql://{{username}}:{{password}}@db.mycompany.com:5432/myapp" \
allowed_roles="myapp-role" \
username="vault_admin" \
password="vault_admin_password"
# Create a role that generates credentials with a 1-hour TTL
vault write database/roles/myapp-role \
db_name=myapp-db \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="2h"
# In your pipeline, fetch dynamic credentials
# vault read database/creds/myapp-role
# Returns a unique username/password pair valid for 1 hour
تُزيل الأسرار الديناميكية مشكلة تدوير بيانات الاعتماد بالكامل. يحصل كل تشغيل لخط الأنابيب على بيانات اعتماد فريدة خاصة به، وتنتهي صلاحية بيانات الاعتماد المكشوفة تلقائياً.
بيانات الاعتماد قصيرة العمر وهوية أحمال العمل
أهم تطور في إدارة أسرار CI/CD في السنوات الأخيرة هو اتحاد هوية أحمال العمل (Workload Identity Federation) — قدرة منصة CI/CD على المصادقة مباشرة مع مزود السحابة باستخدام هويتها الخاصة، دون أي بيانات اعتماد مخزنة.
GitHub Actions OIDC مع AWS
يمكن لـ GitHub Actions تولي دور AWS IAM مباشرة باستخدام اتحاد OIDC. لا يتم تخزين مفاتيح وصول AWS في أي مكان.
# First, create an OIDC identity provider in AWS (via Terraform)
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["6938fd4d98bab03faadb97b34396831e3780aea1"]
}
# Create an IAM role that GitHub Actions can assume
resource "aws_iam_role" "github_actions" {
name = "github-actions-deploy"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Federated = aws_iam_openid_connect_provider.github.arn
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
StringLike = {
"token.actions.githubusercontent.com:sub" = "repo:my-org/my-repo:ref:refs/heads/main"
}
}
}]
})
}
# GitHub Actions workflow using OIDC
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy
aws-region: us-east-1
role-duration-seconds: 900 # 15 minutes
- name: Deploy
run: aws s3 sync ./build s3://my-bucket
GitHub Actions OIDC مع GCP
تدعم Google Cloud نفس النمط من خلال Workload Identity Federation.
# Create a Workload Identity Pool and Provider (gcloud CLI)
gcloud iam workload-identity-pools create "github-pool" \
--project="my-project" \
--location="global" \
--display-name="GitHub Actions Pool"
gcloud iam workload-identity-pools providers create-oidc "github-provider" \
--project="my-project" \
--location="global" \
--workload-identity-pool="github-pool" \
--display-name="GitHub Provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
--attribute-condition="assertion.repository_owner == 'my-org'" \
--issuer-uri="https://token.actions.githubusercontent.com"
# Grant the Workload Identity the ability to impersonate a service account
gcloud iam service-accounts add-iam-policy-binding \
deploy-sa@my-project.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/github-pool/attribute.repository/my-org/my-repo"
# GitHub Actions workflow for GCP
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/123456/locations/global/workloadIdentityPools/github-pool/providers/github-provider
service_account: deploy-sa@my-project.iam.gserviceaccount.com
- name: Deploy to Cloud Run
run: gcloud run deploy my-service --image=gcr.io/my-project/my-app:latest
اتحاد OIDC في GitLab CI
يدعم GitLab CI نفس نمط اتحاد OIDC مع AWS وGCP وAzure. التكوين مشابه — تقوم بتكوين مزود السحابة ليثق بمُصدر OIDC الخاص بـ GitLab وربط الوصول بمعرفات مشاريع أو فروع أو بيئات محددة.
# GitLab CI with AWS OIDC
assume_role:
stage: deploy
id_tokens:
AWS_OIDC_TOKEN:
aud: https://sts.amazonaws.com
script:
- >
STS_CREDS=$(aws sts assume-role-with-web-identity
--role-arn arn:aws:iam::123456789012:role/gitlab-deploy
--role-session-name "gitlab-ci-${CI_PIPELINE_ID}"
--web-identity-token "${AWS_OIDC_TOKEN}"
--duration-seconds 900)
- export AWS_ACCESS_KEY_ID=$(echo $STS_CREDS | jq -r '.Credentials.AccessKeyId')
- export AWS_SECRET_ACCESS_KEY=$(echo $STS_CREDS | jq -r '.Credentials.SecretAccessKey')
- export AWS_SESSION_TOKEN=$(echo $STS_CREDS | jq -r '.Credentials.SessionToken')
- aws s3 sync ./build s3://my-bucket
لماذا تتفوق بيانات الاعتماد قصيرة العمر
مزايا بيانات الاعتماد المتحدة قصيرة العمر مقارنة بالأسرار المخزنة طويلة الأمد كبيرة:
- لا أسرار لسرقتها. لا توجد بيانات اعتماد مخزنة لتسريبها. يقوم خط الأنابيب بالمصادقة باستخدام رمز JWT موقّع صالح فقط لذلك التشغيل المحدد.
- لا حاجة للتدوير. يتم إنشاء بيانات الاعتماد لكل تشغيل وتنتهي صلاحيتها تلقائياً. لا يوجد شيء للتدوير.
- نطاق دقيق. يمكن تقييد الوصول بمستودعات وفروع وبيئات وحتى مهام سير عمل محددة.
- مسار تدقيق كامل. تُظهر سجلات مزود السحابة بالضبط أي تشغيل لخط الأنابيب وصل إلى أي موارد، مرتبطاً بمطالبة OIDC.
- تقليل نطاق الضرر. حتى لو تم تسريب بيانات اعتماد بطريقة ما، فإنها تنتهي في دقائق وليس أشهر.
الأنماط المضادة التي يجب تجنبها
معرفة ما لا يجب فعله لا يقل أهمية عن معرفة الأنماط الصحيحة. تُلاحظ هذه الأنماط المضادة بانتظام في بيئات الإنتاج.
استخدام رموز الوصول الشخصية في CI
تُعد رموز الوصول الشخصية (PATs) المرتبطة بحسابات المطورين الأفراد أحد أكثر الأنماط شيوعاً وأخطرها. عندما يغادر مطور المؤسسة، قد يستمر رمز PAT الخاص به في العمل. عادةً ما تتمتع رموز PATs بصلاحيات واسعة — أكثر بكثير مما يحتاجه خط الأنابيب. إذا تم تسريبه، يحصل المهاجم على وصول إلى كل ما يمكن لذلك المطور الوصول إليه.
بدلاً من ذلك: استخدم حسابات آلية مع رموز محددة النطاق، أو الأفضل من ذلك، استخدم رموز تثبيت GitHub App أو اتحاد OIDC.
مشاركة الأسرار عبر البيئات
استخدام نفس كلمة مرور قاعدة البيانات للتطوير والاختبار والإنتاج — أو نفس مفتاح API لجميع البيئات — يعني أن اختراق أقل بيئاتك تأميناً (عادةً بيئة التطوير) يمنح المهاجمين وصولاً إلى الإنتاج. فصل البيئات لا معنى له إذا كانت بيانات الاعتماد متطابقة.
بدلاً من ذلك: استخدم أسراراً محددة النطاق حسب البيئة. في GitHub Actions، قم بتكوين بيئات النشر مع مخازن أسرار خاصة بها. في GitLab، استخدم المتغيرات المحمية المحددة لبيئات معينة.
عدم تدوير الأسرار بعد الكشف
عندما يتم تسجيل سر عن طريق الخطأ، أو إضافته إلى مستودع، أو كشفه في مخرجات بناء، تكتفي العديد من الفرق بحذف السجل أو إزالة الـ commit دون تدوير بيانات الاعتماد. هذا غير كافٍ. يجب أن تفترض أن السر قد تم رصده وتقوم بتدويره فوراً.
بدلاً من ذلك: تعامل مع أي كشف على أنه اختراق. قم بالتدوير فوراً. أتمت التدوير حيثما أمكن. استخدم الأسرار الديناميكية لجعل المشكلة غير ذات صلة.
الوثوق بـ pull_request_target مع الأسرار
يعمل حدث pull_request_target في GitHub Actions في سياق الفرع الأساسي، مما يعني أنه يمتلك وصولاً إلى الأسرار. هذا مُصمم لعمليات آمنة مثل تصنيف طلبات السحب. ومع ذلك، إذا قام سير عملك بسحب مرجع رأس طلب السحب وتشغيل ذلك الكود، فقد منحت مساهماً خارجياً وصولاً كاملاً إلى أسرارك.
# DANGEROUS: This gives the PR author access to all repository secrets
on: pull_request_target
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # Checking out untrusted code!
- run: make test # Running untrusted code with access to secrets!
بدلاً من ذلك: لا تقم أبداً بسحب وتنفيذ كود طلب السحب في سير عمل pull_request_target. إذا كنت بحاجة لتشغيل اختبارات على كود طلب السحب مع الأسرار، استخدم نهج سير العمل المزدوج: شغّل الكود غير الموثوق في سير عمل pull_request (بدون أسرار)، ثم استخدم مشغل workflow_run منفصل للعمليات الموثوقة.
الدفاع المتعدد الطبقات: نهج متعدد المستويات
لا يكفي أي تحكم واحد بمفرده. تتطلب إدارة الأسرار الفعالة طبقات دفاعية متعددة ومتداخلة.
فحص الأسرار
نفّذ الفحص في ثلاث مراحل:
- قبل الـ commit: استخدم أدوات مثل
gitleaksأوdetect-secretsكخطافات pre-commit لمنع الأسرار من الدخول إلى المستودع أصلاً. - داخل خط الأنابيب: شغّل فحص الأسرار كخطوة CI في كل طلب سحب. يمكن لأدوات مثل
trufflehogفحص الفروقات وسجل الـ commits وحتى الملفات الثنائية. - بعد الـ commit: فعّل فحص الأسرار المدمج في GitHub أو كشف الأسرار في GitLab لفحص محتوى المستودع باستمرار والتنبيه عند العثور على نتائج.
# Pre-commit hook with gitleaks
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.21.2
hooks:
- id: gitleaks
# In-pipeline scanning with trufflehog
- name: Scan for secrets
run: |
docker run --rm -v "$PWD:/repo" trufflesecurity/trufflehog:latest \
git file:///repo --only-verified --fail
تسجيل التدقيق للوصول إلى الأسرار
يجب تسجيل كل وصول إلى سر. يوفر Vault سجلات تدقيق مفصلة بشكل افتراضي. يتكامل مديرو أسرار مزودي السحابة (AWS Secrets Manager وGCP Secret Manager) مع CloudTrail وCloud Audit Logs على التوالي. بالنسبة لأسرار المنصة الأصلية، فعّل ميزات سجل التدقيق المتاحة في GitHub Enterprise أو GitLab Ultimate.
# Enable Vault audit logging
vault audit enable file file_path=/var/log/vault/audit.log
# Each access generates a log entry like:
# {"type": "response", "auth": {"token_type": "service", "policies": ["ci-deploy"]},
# "request": {"path": "secret/data/myapp/db", "operation": "read"}, ...}
تطبيق مبدأ الحد الأدنى من الصلاحيات
طبّق مبدأ الحد الأدنى من الصلاحيات بشكل صارم:
- حدد نطاق الأسرار للمستودع المحدد الذي يحتاجها، وليس المؤسسة.
- استخدم أسرار مستوى البيئة بحيث تكون بيانات اعتماد الإنتاج متاحة فقط لسير العمل الذي ينشر في الإنتاج.
- قم بتكوين حماية الفروع بحيث يمكن فقط لسير العمل الذي يعمل على الفروع المحمية الوصول إلى أسرار الإنتاج.
- في Vault، اكتب سياسات تمنح الوصول إلى أضيق مسار ممكن مع صلاحيات القراءة فقط.
# Vault policy: minimal access for a specific microservice's CI
path "secret/data/payments-service/production" {
capabilities = ["read"]
}
# Deny access to everything else by default (Vault's default behavior)
# No wildcards, no broad paths
التدوير التلقائي
يجب تدوير الأسرار الثابتة وفق جدول منتظم وفوراً بعد أي كشف مشتبه به. أتمت هذه العملية:
- استخدم الأسرار الديناميكية في Vault لإلغاء الحاجة إلى التدوير بالكامل.
- بالنسبة للأسرار التي يجب أن تكون ثابتة (مثل مفاتيح API لجهات خارجية)، استخدم التدوير المدمج في AWS Secrets Manager مع دوال Lambda أو حلول سحابية أصلية مشابهة.
- نفّذ تنبيهات للأسرار التي لم يتم تدويرها خلال عمرها المتوقع.
# AWS Secrets Manager: configure automatic rotation
aws secretsmanager rotate-secret \
--secret-id myapp/api-key \
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:rotate-api-key \
--rotation-rules '{"ScheduleExpression": "rate(30 days)"}'
الخلاصة: إدارة الأسرار عملية مستمرة
إدارة الأسرار ليست خانة يتم تحديدها أثناء الإعداد الأولي لخط الأنابيب. إنها ممارسة مستمرة يجب أن تتطور مع نمو بنيتك التحتية، ومع ظهور تقنيات هجوم جديدة، ومع تغير فريقك. تمثل الأنماط الموصوفة في هذا الدليل — اتحاد OIDC، والأسرار الديناميكية، والحقن الفوري، وتحديد نطاق الحد الأدنى من الصلاحيات، والفحص متعدد الطبقات — أحدث ما توصلت إليه الممارسات الحالية، لكنها تتطلب اهتماماً مستمراً.
ابدأ بتدقيق خطوط الأنابيب الحالية. حدد كل بيانات اعتماد مخزنة. لكل واحد منها، اسأل: هل يمكن استبدال هذا ببيانات اعتماد قصيرة العمر أو اتحاد هوية أحمال العمل؟ هل يمكن تضييق هذا النطاق؟ هل يتم تسجيل هذا السر في أي مكان؟ هل يوجد مسار تدقيق لكل وصول؟
المؤسسات التي تعاني من اختراقات CI/CD ليست تلك التي لم تخزن سراً أبداً — فهذا مستحيل. إنها تلك التي تعاملت مع إدارة الأسرار كمهمة تكوين لمرة واحدة بدلاً من ممارسة أمنية حية. ابنِ الأتمتة، وطبّق السياسات، وراقب سجلات الوصول، وكرر العملية. ستكون خطوط الأنابيب الخاصة بك أصعب بكثير في الاختراق نتيجة لذلك.