Securing Service Principals and Managed Identities in Entra ID Workload Identities | الهوية اللي محدش بيحميها: أمّن Service Principals و Managed Identities في Entra ID
الهوية اللي محدش بيحميها: أمّن Service Principals و Managed Identities في Entra ID
دليل عملي لاكتشاف وتأمين الـ Workload Identities في Tenant بتاعك قبل ما تتحول لثغرة سايبة لأي حد يستغلها.
المقدمة
كنت بعمل Security Review لـ Tenant عميل، ولقيت Service Principal معاه App Role اسمه Mail.Send على مستوى الـ Tenant كله، والـ Secret بتاعه عمره أكتر من سنتين، وحد أنشأه اسمه مش موجود في الشركة من زمان. حد ما كان عارف الـ App ده بيعمل ايه أصلاً. ده مثال بسيط جداً على مشكلة أكبر: احنا بنحمي حسابات البني آدمين بـ MFA وConditional Access وPIM، وفي نفس الوقت عندنا مئات الـ Service Principals وManaged Identities اللي عندها صلاحيات فوق الوصف، ومفيش حد بيراقبها لأنها "مش هيوزر" في نظرنا. الهوية دي بالذات هي أسهل نقطة دخول لأي attacker دلوقتي، لأنها غالباً مش عليها MFA، ومفيش عليها Conditional Access افتراضياً، وأحياناً الـ Secret بتاعها متسرب في كود على GitHub من سنين.
- Entra ID P1 كحد أدنى، وEntra Workload ID Premium (أو Entra ID Governance) عشان تطبق Conditional Access على الـ Workload Identities.
- رول Application Administrator أو Cloud Application Administrator على الأقل، أو Global Administrator للتعديلات الكبيرة.
- Microsoft Graph PowerShell SDK متركب (
Install-Module Microsoft.Graph).- Access لـ Microsoft Defender XDR أو Microsoft Sentinel لو عايز تعمل Hunting بالـ KQL.
الفرق بين Service Principal و Managed Identity
قبل ما تأمّن حاجة، لازم تفهم الفرق، لأن كل نوع له طريقة تأمين مختلفة.
| النوع | Credentials | أنسب استخدام |
|---|---|---|
| Service Principal (App Registration) | Client Secret أو Certificate أو Federated Credential، أنت اللي بتديره | تطبيقات تشتغل خارج Azure، أو CI/CD pipelines، أو multi-tenant apps |
| Managed Identity (System-assigned) | مفيش Secret، Azure بيديره بالكامل | Resource واحد داخل Azure (VM, Function App) |
| Managed Identity (User-assigned) | مفيش Secret برضه، لكن قابل للمشاركة بين Resources | عدة Resources محتاجين نفس الهوية |
✅ لو الـ Workload شغالة جوه Azure والـ target هو Graph أو Storage أو Key Vault، استخدم Managed Identity على طول. مفيش سبب حقيقي تستخدم Client Secret لو Managed Identity متاحة.
اكتشاف الـ Workload Identities المعرضة للخطر
1. جرد الـ Service Principals وتواريخ انتهاء الـ Credentials
Connect-MgGraph -Scopes "Application.Read.All"
Get-MgServicePrincipal -All | ForEach-Object {
$sp = $_
$sp.PasswordCredentials + $sp.KeyCredentials | ForEach-Object {
[PSCustomObject]@{
DisplayName = $sp.DisplayName
AppId = $sp.AppId
CredentialType = $_.GetType().Name
EndDate = $_.EndDateTime
}
}
} | Where-Object { $_.EndDate -lt (Get-Date).AddDays(30) -and $_.EndDate -gt (Get-Date) } |
Sort-Object EndDate | Format-Table -AutoSize
ده هيديك أي Secret أو Certificate هيموت في أقل من 30 يوم، وده مهم لأنه بيمنعك من مفاجآت تفصل تطبيق production مفيش حد فاكره.
2. Hunting على Sign-ins الغريبة للـ Service Principals
لو عندك Sentinel أو Defender XDR متوصل بـ Entra ID logs، استخدم الـ KQL ده للـ sign-ins اللي جاية من IP غير معتاد أو خارج ساعات الشغل المتوقعة:
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| summarize Countries = make_set(Location), IPs = make_set(IPAddress), Count = count()
by ServicePrincipalName, AppId
| where array_length(IPs) > 3
| order by Count desc
لو شفت Service Principal واحد بيسجّل من أكتر من 3 IPs مختلفين جغرافياً في أسبوع، ده مش طبيعي لأداة CI/CD أو Managed Identity، ده يستحق تحقيق.
تأمين بالـ Conditional Access for Workload Identities
من مميزات Entra ID Premium إنك تقدر تطبق Conditional Access مباشرة على Service Principals (مش Managed Identities للأسف، دي حالياً محدودة). ده بيخليك تمنع Service Principal معين من السجيل إلا من IP ranges معينة، أو تفرض إنه يعدي على Named Locations محددة.
1. إنشاء Policy عبر Microsoft Graph
POST https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies
Content-Type: application/json
{
"displayName": "CA - Block SP sign-in outside trusted locations",
"state": "enabled",
"conditions": {
"clientAppTypes": ["all"],
"servicePrincipalRiskLevels": [],
"applications": {
"includeApplications": ["All"]
},
"locations": {
"includeLocations": ["All"],
"excludeLocations": [""]
},
"clientApplications": {
"includeServicePrincipals": [""]
}
},
"grantControls": {
"operator": "OR",
"builtInControls": ["block"]
}
}
⚠️ لازم Workload ID Premium license، ولو مش موجودة الـ policy مش هتشتغل صح حتى لو ظهرت إنها enabled في الـ portal. اتأكد من الـ license status الأول قبل ما تفتكر إن الحماية شغالة.
التخلص من Secrets: Federated Credentials بدل Client Secrets
لو عندك GitHub Actions أو Azure DevOps Pipeline بيستخدم Client Secret عشان يعمل login كـ Service Principal، أنت بتحمل مسؤولية rotation ومسؤولية تسريب. الحل الأفضل هو Workload Identity Federation، اللي بتسمح للـ pipeline يستخدم OIDC token بدل Secret خالص.
إنشاء Federated Credential
New-MgApplicationFederatedIdentityCredential -ApplicationId "" -BodyParameter @{ name = "github-actions-federated" issuer = "https://token.actions.githubusercontent.com" subject = "repo:my-org/my-repo:ref:refs/heads/main" audiences = @("api://AzureADTokenExchange") }
✅ لما تنجح الخطوة دي، هتلاقي الـ pipeline بيعمل login بدون أي Secret مخزن في أي مكان، وده بيقفل واحد من أكتر مصادر التسريب انتشاراً في الشركات.
الـ Secrets اللي لازم تفضل: Key Vault و PIM for Groups
مش كل حاجة تقدر تحولها لـ Federated Credential أو Managed Identity، فيه أنظمة legacy لسه محتاجة Client Secret. في الحالة دي:
- خزّن الـ Secret في Azure Key Vault مع Access Policy محدد بدقة، مش Contributor على الـ Vault كله.
- فعّل rotation policy على الـ Secret بحد أقصى 90 يوم.
- لو فيه Group بصلاحيات إدارة الـ Service Principals نفسها، ضيفها لـ PIM for Groups عشان الـ activation يكون time-bound ومراقب بـ approval.
مشاكل شائعة وحلولها
| المشكلة | الحل |
|---|---|
| Conditional Access policy مش بتطبق على Service Principal | تأكد إن الـ Workload ID Premium مفعّلة، وإن الـ Service Principal مضاف بالـ Object ID الصحيح مش الـ App ID |
| Managed Identity مش قادرة توصل لـ Key Vault | تأكد من Access Policy أو RBAC role (Key Vault Secrets User) للـ Identity نفسها، مش للـ Resource |
| Federated Credential بيرفض الـ token | راجع الـ subject claim، لازم يطابق بالضبط branch أو environment اللي بيشتغل منه الـ pipeline |
| Service Principal قديم محدش عارف بيعمل ايه | استخدم Sign-in logs لآخر 90 يوم، لو مفيش نشاط عطّله (disable) قبل ما تحذفه نهائي |
نصائح الأمان 🔐
- راجع الـ API Permissions (App Roles) لكل Service Principal دورياً، وشيل أي permission مش مستخدم فعلياً، خصوصاً Application Permissions زي
Directory.ReadWrite.All. - لا تعطي Service Principal رول Global Administrator أبداً، استخدم Custom Roles محددة النطاق أو Administrative Units.
- فعّل Alert في Defender XDR أو Sentinel لأي إضافة Credential جديد على Service Principal حساس (زي اللي عنده Mail.Send أو RoleManagement.ReadWrite.All).
- حدد Owner واضح لكل Application Registration من أول يوم، الـ Ownerless Apps هي أكتر حاجة بتضيع في الـ audit.
- استخدم Access Reviews على الـ App Role Assignments مش فقط على الـ Group Memberships.
الخلاصة
الـ Workload Identities مش مجرد تفصيلة تقنية جانبية، دي طبقة كاملة من الهويات في Tenant بتاعك، وأغلب الشركات بتحميها أقل بكتير من حسابات البني آدمين. الخطوة الأولى دايماً هي الظهور: تعرف عندك ايه، وبعدين تقفل الـ gaps واحدة واحدة.
- اجرد كل Service Principals وMangaged Identities وتواريخ انتهاء الـ Credentials.
- طبق Conditional Access on Workload Identities على الحساسة منهم.
- انقل من Client Secrets لـ Federated Credentials أو Managed Identities كل ما يكون ممكن.
- راقب الـ sign-ins بالـ KQL بشكل دوري، مش مرة واحدة وخلاص.
إنت عملت audit لـ Service Principals بتاعتك آخر مرة إمتى؟ شاركنا تجربتك في التعليقات.

Comments
Post a Comment