Entra Service Principal: Rilevamento delle Anomalie di Accesso, Identità del Carico di Lavoro Definita
Entra Service Principal: il rilevamento delle anomalie di accesso, il monitoraggio dell'identità del carico di lavoro che stabilisce una baseline di come si autenticano gli account macchina, è il controllo che la maggior parte dei tenant non configura mai. Ogni altro controllo di identità in un tenant maturo — campagne di iscrizione MFA, metodi di autenticazione resistenti al phishing, Conditional Access basato sul rischio — è costruito attorno a un essere umano presente alla tastiera. I service principal non hanno un umano, e questi controlli semplicemente non li raggiungono.
Microsoft è esplicita sul perché queste identità sono diverse. Un'identità del carico di lavoro è un'identità che consente a un'applicazione o a un service principal di accedere a risorse, e la documentazione di Microsoft stessa elenca tre proprietà che la distinguono da un account utente: le identità del carico di lavoro "Non possono eseguire l'autenticazione multifattore", "Spesso non hanno un processo di ciclo di vita formale", e "Devono memorizzare le proprie credenziali o segreti da qualche parte".
Leggendo questi tre punti insieme, l'esposizione diventa evidente. La credenziale è una stringa depositata in una variabile di pipeline, un Key Vault, un file appsettings, o un layer di immagine container. Non c'è alcun secondo fattore dietro di essa, nessun ciclo di vita che la faccia scadere all'ultimo giorno di un dipendente che lascia l'azienda, e — poiché un accesso di service principal non coinvolge alcun utente — nulla che appaia insolito a uno stack di alert centrato sull'umano. Una volta che quella stringa trapela, l'attaccante si autentica esattamente come fa l'applicazione, e l'accesso ha successo al primo tentativo.
⚠️ Avviso: Dopo che un segreto di service principal trapela non c'è password da spruzzare, nessun prompt MFA da affaticare e nessun utente da phishare. L'unico segnale che rimane è la forma degli accessi: quanti, da dove, su quale rete, con quale credenziale.
Perché la Tua Copertura MFA e Conditional Access Non Si Applica
Il Conditional Access è il controllo che gli amministratori presumono copra tutto. Non è così — le identità del carico di lavoro sono uno dei diversi punti in cui la sua copertura si interrompe silenziosamente, insieme agli errori di ambito ed esclusione catalogati in Lacune Accesso Condizionale Entra ID. La documentazione di Microsoft afferma che le policy di Conditional Access "storicamente si applicavano solo agli utenti quando accedevano ad app e servizi", e che il supporto per i service principal è una capacità separata chiamata Conditional Access per le identità del carico di lavoro.
Quella capacità separata comporta restrizioni che determinano se la tua policy fa effettivamente qualcosa:
| Vincolo | Cosa afferma la documentazione di Microsoft |
|---|---|
| Licenza | "Sono richieste licenze Workload Identities Premium per creare o modificare policy di Conditional Access mirate ai service principal." Le policy esistenti continuano a funzionare in directory senza licenza ma non possono essere modificate. |
| Ambito | "La policy può essere applicata a service principal single-tenant registrati nel tuo tenant. Le applicazioni SaaS Microsoft e di terze parti, incluse le app multi-tenant, non sono coperte da queste policy. Le identità gestite non sono coperte dalla policy." |
| Assegnazione tramite gruppo | "Sebbene i service principal possano essere aggiunti a gruppi, le policy di Conditional Access assegnate a un gruppo che contiene un service principal non vengono applicate a quel service principal." Deve essere assegnata direttamente come identità del carico di lavoro. |
| Controlli disponibili | Sotto Concedi, "Blocca accesso è l'unica opzione disponibile." Non c'è un "richiedi MFA" a cui ricorrere. |
La regola dell'assegnazione tramite gruppo merita di essere sottolineata, perché fallisce silenziosamente. Un amministratore che inserisce service principal in un gruppo di sicurezza già bersaglio di una policy di Conditional Access vedrà una policy che sembra correttamente ambito nel portale e che non applica assolutamente nulla a quei service principal. Nulla genera errore. Nulla avvisa. La policy semplicemente non viene valutata per loro.
Le condizioni supportate sono volutamente ristrette: bloccare i service principal "al di fuori di intervalli IP pubblici conosciuti", bloccare "in base al rischio rilevato da Microsoft Entra ID Protection", e i contesti di autenticazione. Posizione e rischio — non il dispositivo, non l'MFA, non la conformità.
Esiste anche un'asimmetria tra rilevare il rischio e bloccarlo, facile da invertire. ID Protection "rileva il rischio su app single-tenant, SaaS non-Microsoft, e multi-tenant", mentre l'applicazione del Conditional Access raggiunge solo i service principal single-tenant. Le identità gestite sono fuori da entrambi. Quindi per un'applicazione SaaS multi-tenant nel tuo tenant, puoi essere informato che è compromessa senza poterla bloccare con una policy di identità del carico di lavoro — il che rende il rilevamento e la risposta, non la prevenzione, il controllo operante.
Dove Vivono Realmente gli Accessi dei Service Principal
La seconda ragione per cui questi accessi non vengono sorvegliati è che la maggior parte delle query di hunting non li tocca mai. Microsoft Entra registra quattro tipi di log di accesso: accessi utente interattivi, accessi utente non interattivi, accessi di service principal, e accessi di identità gestita. Un SOC che ha costruito le proprie rilevazioni sugli accessi utente non sta guardando un sottoinsieme filtrato di una tabella — sta guardando un flusso completamente diverso.
Il flusso dei service principal è a sé stante. Microsoft lo descrive chiaramente: "A differenza degli accessi utente interattivi e non interattivi, gli accessi di service principal non coinvolgono un utente. Sono invece accessi di qualsiasi account non-utente, come app o service principal... In questi accessi, l'app o il servizio fornisce la propria credenziale, come un certificato o un segreto applicativo, per autenticarsi o accedere a risorse."
Quando esportati in un'area di lavoro Log Analytics tramite le Impostazioni diagnostiche, questi finiscono nella tabella AADServicePrincipalSignInLogs — non SigninLogs. Se non hai mai abilitato quella categoria di log, le query della sezione successiva restituiscono zero righe, e zero righe sembreranno esattamente un ambiente pulito.
La retention è l'altra metà del prerequisito. Il riferimento di retention dei dati di Microsoft assegna ai log di accesso sette giorni su Microsoft Entra ID Free e 30 giorni su P1 o P2. Un rilevamento comportamentale ha bisogno di una baseline più una finestra recente entro quel budget — non si può costruire un profilo di 90 giorni da un log di 30 giorni. La configurazione dell'esportazione è trattata in Logging Entra ID: Retention, Diagnostic Settings, Lacune di Export SIEM; trattalo come un prerequisito rigido per tutto ciò che segue.
Queste sono le colonne che portano il segnale:
| Colonna | Perché conta |
|---|---|
AppId, ServicePrincipalName | L'identità su cui costruire la baseline. AppId è la chiave di raggruppamento stabile. |
IPAddress, LocationDetails, AutonomousSystemNumber | Da dove si è autenticato il carico di lavoro. La rete di un carico di lavoro è di solito molto più stabile di quella di un umano. |
ServicePrincipalCredentialKeyId, ClientCredentialType | Quale credenziale è stata usata, e se era un client secret o un client assertion. |
ResourceDisplayName, ResourceIdentity | Cosa ha raggiunto. Nuove risorse target sono un segnale di pivot forte. |
ResultType | "0" per successo. I fallimenti sono un'indagine diversa. |
ConditionalAccessStatus | Se una policy di identità del carico di lavoro è mai stata valutata. |
FederatedCredentialId | Popolato quando è stata usata una credenziale di identità federata al posto di un segreto memorizzato. |
La Catena d'Attacco Dopo che un Segreto Trapela
Passo 1 — La credenziale sfugge
Il segreto lascia l'ambiente: committato in un repository pubblico, catturato da un log di build, estratto da una workstation sviluppatore compromessa, o acquistato. La rilevazione Leaked Credentials di Microsoft per le identità del carico di lavoro esiste proprio perché questo è routine — verifica le credenziali acquisite "da GitHub, il dark web, siti paste, o altre fonti" contro le credenziali valide nel tenant. Lo stesso servizio copre gli account utente, dove il fallimento ricorrente riguarda la risposta piuttosto che il rilevamento — vedi Entra ID Risk Protection: Credenziali Trapelate, Utenti a Rischio Non Risolti.
Passo 2 — Un'autenticazione che sembra interamente legittima
L'attaccante esegue un flusso OAuth client credentials con il segreto rubato. Nessun MFA è possibile, quindi nessuno manca. Se nessuna policy di identità del carico di lavoro è assegnata direttamente, ConditionalAccessStatus riflette un accesso che nulla ha valutato. Il risultato è un accesso riuscito che è, byte per byte, la stessa operazione che l'applicazione reale esegue mille volte al giorno. Solo il contesto circostante differisce.
Passo 3 — Persistenza tramite aggiunta di una credenziale
Piuttosto che affidarsi a un segreto che potrebbe essere ruotato, un attaccante con privilegi sufficienti aggiunge la propria credenziale all'oggetto applicazione o service principal. L'identità ora ha due credenziali valide, e revocare quella trapelata non cambia nulla. Questo passo lascia due tracce: una modifica di directory nel log di audit, e — a ogni accesso successivo — un ServicePrincipalCredentialKeyId mai apparso prima per quell'AppId.
Passo 4 — Agire tramite Graph
I permessi applicativi del service principal sono ora il raggio d'impatto, e i permessi applicativi non sono vincolati dai diritti di alcun utente. Concessioni a livello di tenant come Directory.ReadWrite.All o RoleManagement.ReadWrite.Directory trasformano un segreto trapelato in controllo sull'intera directory; vedi Entra App Registration: Permessi Graph API Pericolosi su come queste concessioni vengono inventariate e ridotte. La rilevazione Suspicious API Traffic di Microsoft mira a questa fase, scattando "quando viene osservato traffico GraphAPI anomalo o enumerazione di directory da parte di un service principal".
Rilevamento
Il rilevamento comportamentale qui si basa su una proprietà che gioca a favore del difensore: i carichi di lavoro sono noiosamente coerenti. Un job di sincronizzazione notturno viene eseguito alla stessa ora, dallo stesso IP di uscita, contro la stessa risorsa, con la stessa credenziale. Gli umani sono erratici; l'automazione no. Questa coerenza rende la deviazione significativa — ed è per questo che le rilevazioni equivalenti per gli account umani, come il viaggio impossibile e il replay di token AiTM, devono tollerare molto più rumore delle query qui sotto.
| Indicatore | Dove guardare | Campi chiave | Cosa significa |
|---|---|---|---|
| Picco di volume di accessi | AADServicePrincipalSignInLogs | AppId, conteggio nel tempo | Raccolta massiva o enumerazione tramite un token rubato |
| Paese o ASN visto per la prima volta | AADServicePrincipalSignInLogs | LocationDetails, AutonomousSystemNumber | Il carico di lavoro si è autenticato da un'infrastruttura mai usata prima |
| Nuova credenziale in uso | AADServicePrincipalSignInLogs | ServicePrincipalCredentialKeyId, ClientCredentialType | Una credenziale aggiunta da un attaccante, o una rotazione non gestita |
| Nuova risorsa target | AADServicePrincipalSignInLogs | ResourceDisplayName | L'identità sta raggiungendo oltre il suo ruolo abituale |
| Credenziale aggiunta all'oggetto | Log di audit Entra | Modifiche credenziali applicazione / service principal | Il passo di persistenza del Passo 3 |
| Credenziale trapelata / accesso sospetto | Rilevazioni di identità del carico di lavoro di ID Protection | riskyServicePrincipals, servicePrincipalRiskDetections | Correlazione lato Microsoft che non devi costruire |
Picco di volume rispetto a una baseline per identità
// Workload identity volume spike: last 24h vs the preceding daily baseline
let Recent = 1d;
let Lookback = 30d;
let MinRecent = 20; // suppress low-volume identities
let MinBaselineDays = 7; // suppress identities we have not observed long enough
let Baseline =
AADServicePrincipalSignInLogs
| where TimeGenerated between (ago(Lookback) .. ago(Recent))
| where ResultType == "0"
| summarize Total = count(), Days = dcount(startofday(TimeGenerated)) by AppId
| where Days >= MinBaselineDays and Total > 0
| extend BaselineDaily = todouble(Total) / todouble(Days);
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(Recent)
| where ResultType == "0"
| summarize RecentCount = count(), Name = take_any(ServicePrincipalName) by AppId
| where RecentCount >= MinRecent
| join kind=inner (Baseline) on AppId
| extend SpikeRatio = round(RecentCount / BaselineDaily, 1)
| where SpikeRatio >= 5
| project Name, AppId, RecentCount, BaselineDaily = round(BaselineDaily, 1), SpikeRatio
| order by SpikeRatio desc
Paese o sistema autonomo visto per la prima volta
// A workload identity authenticating from a country or ASN it has never used
let Recent = 1d;
let Lookback = 30d;
let Known =
AADServicePrincipalSignInLogs
| where TimeGenerated between (ago(Lookback) .. ago(Recent))
| where ResultType == "0"
| extend Country = tostring(parse_json(tostring(LocationDetails)).countryOrRegion)
| where isnotempty(Country)
| summarize KnownCountries = make_set(Country),
KnownASNs = make_set(AutonomousSystemNumber) by AppId;
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(Recent)
| where ResultType == "0"
| extend Country = tostring(parse_json(tostring(LocationDetails)).countryOrRegion)
| where isnotempty(Country)
| join kind=inner (Known) on AppId
| where array_length(KnownCountries) > 0
| where not(set_has_element(KnownCountries, Country))
or not(set_has_element(KnownASNs, AutonomousSystemNumber))
| project TimeGenerated, ServicePrincipalName, AppId, Country,
ASN = AutonomousSystemNumber, IPAddress, ResourceDisplayName,
NewCountry = not(set_has_element(KnownCountries, Country))
| order by TimeGenerated desc
Una credenziale mai usata prima
// Sign-in using a credential key this workload identity has never presented
let Recent = 1d;
let Lookback = 30d;
let KnownKeys =
AADServicePrincipalSignInLogs
| where TimeGenerated between (ago(Lookback) .. ago(Recent))
| where ResultType == "0" and isnotempty(ServicePrincipalCredentialKeyId)
| summarize KnownKeyIds = make_set(ServicePrincipalCredentialKeyId) by AppId;
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(Recent)
| where ResultType == "0" and isnotempty(ServicePrincipalCredentialKeyId)
| join kind=inner (KnownKeys) on AppId
| where not(set_has_element(KnownKeyIds, ServicePrincipalCredentialKeyId))
| project TimeGenerated, ServicePrincipalName, AppId,
ServicePrincipalCredentialKeyId, ClientCredentialType,
IPAddress, ResourceDisplayName
| order by TimeGenerated desc
Abbina quest'ultima query alla voce del log di audit che registra la credenziale aggiunta all'oggetto applicazione o service principal. Una nuova chiave di credenziale senza una corrispondente richiesta di modifica approvata è il segnale a più alta affidabilità di questo articolo.
💡 Suggerimento: Le soglie sopra (>= 5x, >= 20 accessi recenti, >= 7 giorni di baseline) sono guardrail, non leggi fisiche. Ognuna esiste per eliminare un falso positivo specifico: un'integrazione nuova di zecca senza storico mostrerebbe altrimenti un rapporto infinito, e un singolo accesso contro una baseline di 0,1/giorno si leggerebbe altrimenti come un picco decuplicato. Regolale sui tuoi dati prima di attivare gli alert.
Due limitazioni meritano onestà. Primo, una baseline più corta dell'ideale è inevitabile quando il log stesso scade in 7-30 giorni — se non hai esportato verso un'area di lavoro con retention più lunga, la tua baseline è limitata dalla tua licenza. Secondo, un cambiamento di deployment legittimo (una nuova regione, un build agent migrato, una rotazione pianificata) produce esattamente la stessa firma di un'intrusione. Microsoft riconosce lo stesso effetto nella propria rilevazione: "Gli accessi avviati dopo una modifica di configurazione autorizzata potrebbero attivare questa rilevazione." Queste query producono code di indagine, non verdetti.
Vale la pena abilitare anche la rilevazione Suspicious Sign-ins di Microsoft per le identità del carico di lavoro, insieme a queste. "Apprende le baseline del comportamento di accesso delle identità del carico di lavoro nel tuo tenant", richiede "tra 2 e 60 giorni", e scatta su elementi non familiari "indirizzo IP / ASN, risorsa target, user agent, cambio IP hosting/non-hosting, paese dell'IP, tipo di credenziale" — lo stesso set di funzionalità, correlato lato Microsoft. I clienti senza Workload Identities Premium "continuano a ricevere tutte le rilevazioni con dettagli di reporting limitati".
Remediation / Correzione
💡 Vittoria rapida: Abilita la categoria di log di accesso dei service principal nelle Impostazioni diagnostiche e instradala verso un'area di lavoro Log Analytics. Senza questo, ogni query sopra restituisce zero righe — e zero righe sono indistinguibili da un tenant sano.
-
Esporta il flusso ed estendi la finestra. Instrada i log di accesso dei service principal verso un'area di lavoro la cui retention superi il default di 7 o 30 giorni, così che una baseline sia anche solo possibile. Vedi Logging Entra ID: Retention, Diagnostic Settings, Lacune di Export SIEM.
-
Rimuovi il segreto invece di proteggerlo. Dove il carico di lavoro gira su Azure, usa identità gestite così che la piattaforma gestisca le credenziali. Dove gira fuori da Azure — GitHub Actions, Kubernetes, GCP, AWS — usa la federazione di identità del carico di lavoro, che scambia un token da un provider di identità esterno per un token di accesso. Il ragionamento di Microsoft è diretto: le credenziali memorizzate "rappresentano un rischio di sicurezza e devono essere memorizzate in modo sicuro e ruotate regolarmente", mentre la federazione "elimina il rischio di fuga di segreti o di scadenza dei certificati". Una credenziale che non esiste non può trapelare.
-
Delimita una policy di Conditional Access per identità del carico di lavoro — correttamente. Crea una policy basata sulla posizione sotto Utenti o identità del carico di lavoro → Identità del carico di lavoro, mira a Tutte le risorse, includi Qualsiasi posizione ed escludi le posizioni nominate da cui il tuo carico di lavoro opera legittimamente. Ricorda le tre trappole: richiede Workload Identities Premium, deve essere assegnata al service principal direttamente anziché tramite un gruppo, e Blocca accesso è l'unico controllo di concessione. Salvala prima in modalità Solo report e leggi i risultati nella vista Accessi service principal prima di applicarla.
-
Inventaria e ruota le credenziali. Le linee guida di remediation di Microsoft per un'identità del carico di lavoro compromessa sono di inventariare ogni credenziale sugli oggetti service principal e applicazione, aggiungerne una nuova (certificati x509 preferiti), rimuovere le credenziali compromesse — "Se ritieni che l'account sia a rischio, raccomandiamo di rimuovere tutte le credenziali esistenti" — e ruotare qualsiasi segreto Key Vault che quell'identità potesse raggiungere. L'igiene di rotazione in generale è trattata in Entra App Registration: Rotazione dei Segreti delle Credenziali.
-
Riduci il raggio d'impatto prima di averne bisogno. Il danno che un segreto trapelato può causare è limitato dai permessi applicativi concessi ad esso. Elimina le concessioni Graph a livello di tenant che non sono genuinamente necessarie, e riesamina i service principal che detengono ruoli di directory — il lato della postura statica di questo problema è trattato in Ruolo Admin di Service Principal di Entra Sovraprivilegiato.
-
Verifica. Riesegui le tre query. Un tenant correttamente strumentato restituisce righe corrispondenti ai tuoi eventi di deployment conosciuti e nient'altro; un tenant che non restituisce letteralmente nulla ha un problema di logging, non una pulita cartella clinica. Dove questo controllo si colloca in una revisione completa del tenant è illustrato in Come Auditare la Sicurezza di Microsoft Entra ID (Azure AD): Guida Pratica.
Come EtcSec Rileva Questo
Il motore di audit etc-collector include due detector che coprono esattamente questa telemetria, entrambi classificati Alto:
RISK_SP_SIGNIN_SPIKE— segnala un service principal che ha effettuato accessi molto più frequentemente nelle ultime 24 ore rispetto ai giorni precedenti della finestra raccolta. Il raggruppamento è perappIdsui soli tipi di eventi di accesso macchina, così che un accesso utente interattivo che porti unappIdnon può diluire il tasso misurato di un'applicazione.RISK_UNUSUAL_GEO_ADMIN— segnala un account privilegiato che ha effettuato l'accesso con successo da un paese non usato in precedenza nella finestra raccolta. Questo è il gemello umano della stessa telemetria: la stessa logica di "geografia vista per la prima volta" applicata agli admin invece che ai carichi di lavoro.
Entrambi sono costruiti attorno ai guardrail descritti sopra piuttosto che a un rapporto grezzo: un'identità con troppo poco storico di baseline resta silenziosa, un'identità con troppo pochi accessi recenti resta silenziosa, un insieme di paesi conosciuti vuoto resta silenzioso, e gli accessi senza paese risolto vengono ignorati anziché contati come un nuovo paese. Il filtraggio sul successo differisce tra i due: RISK_UNUSUAL_GEO_ADMIN conta solo gli accessi riusciti, su entrambi i lati del confronto, così da non sovrapporsi a RISK_FAILED_SIGNIN_BURST, che copre già i pattern di tentativi falliti; RISK_SP_SIGNIN_SPIKE misura il suo tasso su ogni accesso macchina della finestra, riuscito o fallito.
La finestra recente è ancorata all'evento raccolto più recente piuttosto che all'orologio reale, così che rigiocare un audit ore dopo confronta gli stessi giorni.
ℹ️ Nota: EtcSec verifica automaticamente questa vulnerabilità durante ogni audit AD/Azure. Esegui un audit gratuito per verificare il tuo ambiente.
Esplora le pagine di identity security collegate a questo tema
