Analyse des journaux de connexion Microsoft entra
L’authentification constitue la ligne de front et le périmètre principal des environnements cloud. Lorsqu’un attaquant cible un tenant Microsoft 365, chaque point de friction, chaque tentative d’intrusion et chaque accès persistant est consigné dans les journaux de connexion de Microsoft Entra ID (Sign-in Logs).
Les journaux de connexion ne se limitent pas à un simple horodatage d’accès ; ils enregistrent l’intégralité de l’état cryptographique et opérationnel de la négociation d’identité : évaluation des politiques d’accès conditionnel, conformité des appareils, méthodes MFA mises en œuvre, scores de risque comportementaux, routage réseau et rédemption des jetons de session.
Cette fiche fournit une dissection technique approfondie des quatre flux de connexion d’Entra ID, de leur schéma JSON, des méthodologies d’investigation et des requêtes de threat hunting en production.
Microsoft Entra ID sépare la télémétrie d’authentification en quatre flux distincts, chacun suivant un acteur et un vecteur de transport spécifiques :
graph TD subgraph "Architecture des Sign-in Logs Entra ID" STS[Entra Security Token Service - STS]
STS -->|1. Saisie d'identifiants par un humain| STREAM1["1. Connexions Utilisateurs Interactives<br/>(SignInLogs)"] STS -->|2. Rédemption de Refresh Token / PRT| STREAM2["2. Connexions Utilisateurs Non Interactives<br/>(AADNonInteractiveUserSignInLogs)"] STS -->|3. Authentification Secret / Certificat| STREAM3["3. Connexions Principaux de Service<br/>(AADServicePrincipalSignInLogs)"] STS -->|4. Identité managée VM / Azure Function| STREAM4["4. Connexions Identités Managées<br/>(AADManagedIdentitySignInLogs)"] endLes 4 flux de connexion
Section intitulée « Les 4 flux de connexion »- Connexions interactives (
interactiveUser) : générées lorsqu’un utilisateur saisit activement un facteur d’authentification (mot de passe, validation d’une notification MFA push, clé FIDO2 ou biométrie Windows Hello for Business). - Connexions non interactives (
nonInteractiveUser) : générées lorsqu’une application cliente (Outlook, Teams, client de synchronisation OneDrive ou script) présente un Refresh Token ou un Primary Refresh Token (PRT) existant pour obtenir silencieusement de nouveaux Access Tokens. Dans 90 % des cas de vol de session et de phishing AiTM, l’activité de l’attaquant réside exclusivement dans ce flux. - Connexions des principaux de service (
servicePrincipal) : générées lorsqu’une application d’entreprise ou un service d’arrière-plan s’authentifie au moyen de son propre secret ou certificat client, sans présence humaine. - Connexions des identités managées (
managedIdentity) : générées par des ressources hébergées dans Azure (machines virtuelles, App Services, fonctions) exploitant une identité assignée pour interroger l’annuaire sans stocker d’identifiants dans le code.
Pourquoi c’est important en DFIR
Section intitulée « Pourquoi c’est important en DFIR »Les journaux de connexion constituent la source de preuve la plus cruciale pour répondre aux questions d’accès initial :
- Détection des attaques par pulvérisation (password spray) et force Brute : identification d’échecs répétés (
50126,50053) répartis sur des centaines de comptes depuis des réseaux de proxys résidentiels. - Mise en évidence du vol de session (AiTM) : une connexion interactive réussit via un reverse proxy malveillant, suivie quelques minutes plus tard de requêtes non interactives émanant d’une IP et d’un ASN totalement différents sans contestation MFA.
- Audit des contournements d’accès conditionnel : vérification des règles CA contournées par l’utilisation d’un protocole d’authentification basique ou l’usurpation d’un emplacement approuvé.
- Surveillance des automatisations malveillantes : détection de l’usage abusif d’un principal de service compromis pour aspirer des données via Microsoft Graph.
Dissection du schéma JSON : les champs clés
Section intitulée « Dissection du schéma JSON : les champs clés »Lors de l’interrogation via Microsoft Graph API (/auditLogs/signIns) ou Azure Log Analytics (SigninLogs), les champs suivants sont fondamentaux pour l’analyste :
| Champ (Graph API) | Champ (Log Analytics) | Signification forensique et interprétation |
|---|---|---|
id | CorrelationId / Id | Identifiant unique de la transaction. Pivot indispensable pour relier la connexion aux événements applicatifs dans l’UAL Purview. |
createdDateTime | TimeGenerated | Horodatage UTC exact de la requête d’authentification au niveau de la passerelle STS Entra. |
userPrincipalName | UserPrincipalName | Identifiant du compte. Toujours croiser avec userId pour détecter les renommages de comptes. |
userId | UserId | GUID immuable de l’objet utilisateur dans l’annuaire. |
appDisplayName | AppDisplayName | Nom affiché de l’application cliente (ex. “Microsoft Office”, “Azure Portal”). |
appId | AppId | GUID applicatif officiel. Un attaquant peut falsifier le nom d’affichage mais pas le GUID appId enregistré. |
ipAddress | IPAddress | Adresse IP source publique (IPv4 ou IPv6). À confronter aux bases de proxys et VPN. |
location | LocationDetails | Pays, région et ville déduits par géolocalisation IP. Contient les coordonnées estimées (geoCoordinates). |
networkLocationDetails | NetworkLocationDetails | Tableau JSON indiquant si le trafic a traversé un Emplacement nommé ou Global Secure access (GSA). |
clientAppUsed | ClientAppUsed | Protocole client : “Browser”, “Mobile Apps and Desktop clients”, “Exchange ActiveSync”, “IMAP4”, “POP3”. |
deviceDetail | DeviceDetail | Objet JSON détaillant deviceId, operatingSystem, browser, isCompliant, isManaged et trustType (“Azure AD joined”). |
conditionalAccessStatus | ConditionalAccessStatus | Résultat global CA : success, failure, notApplied. Doit être détaillé via conditionalAccessPolicies. |
conditionalAccessPolicies | ConditionalAccessPolicies | Tableau de toutes les politiques évaluées. Indique les contrôles exigés (MFA, conformité) et satisfaits. |
riskDetail | RiskDetail | Motif de l’élévation du risque : none, adminGeneratedTemporaryPassword, userReportedSuspiciousActivity. |
riskLevelAggregated | RiskLevelAggregated | Niveau de risque calculé par le Machine Learning : none, low, medium, high, hidden. |
authenticationProcessingDetails | AuthenticationProcessingDetails | Clés/valeurs internes STS : présence d’un PRT (IsPRT: True/False), méthode d’émission, version TLS. |
authenticationRequirement | AuthenticationRequirement | Précise si la transaction imposait singleFactorAuthentication ou multiFactorAuthentication. |
status | ResultType / ResultDescription | Code retour (0 = Succès). Les valeurs non nulles traduisent la cause exacte de l’échec. |
Codes d’erreur d’authentification critiques
Section intitulée « Codes d’erreur d’authentification critiques »L’analyste DFIR doit impérativement maîtriser les codes d’erreur majeurs d’Entra ID pour qualifier les attaques :
| Code d’erreur | Intitulé officiel | Interprétation forensique |
|---|---|---|
0 | Success | Authentification réussie. Jeton émis. |
50053 | Account is locked | Verrouillage du compte déclenché par Smart Lockout suite à une attaque par force brute ou spray. |
50074 | Strong Auth required | L’utilisateur a saisi le bon mot de passe, mais a refusé ou ignoré le challenge MFA. |
50076 | Strong Auth required from user | L’utilisateur doit satisfaire un challenge MFA en raison d’une exigence d’accès conditionnel. |
50088 | Faulty token / Token expired | Le client a présenté un jeton de rafraîchissement invalide ou expiré (fréquent après révocation). |
50097 | Device authentication required | Échec car l’appareil n’est pas reconnu comme géré, conforme ou joint à l’annuaire hybride. |
50126 | Invalid username or password | Mauvais identifiant ou mot de passe. Volume élevé sur une IP = force brute ; sur plusieurs comptes = spray. |
50131 | Device trust failure | Accès bloqué en raison d’un état d’appareil suspect ou non conforme aux exigences MDM. |
53003 | Blocked by Conditional Access | Blocage explicite par une politique d’accès conditionnel (pays interdit, appareil non conforme). |
70044 | Session revoked | Session invalidée par l’administrateur (Revoke-MgUserSignInSession) ou par Continuous Access Evaluation. |
Ce qui est possible vs ce qui n’est pas possible
Section intitulée « Ce qui est possible vs ce qui n’est pas possible »Ce qui est possible
Section intitulée « Ce qui est possible »- Reconstituer la chronologie d’authentification : retracer chaque connexion interactive et rédemption de jeton en arrière-plan sur les 30 derniers jours (P1/P2).
- Prouver le contournement MFA : examiner
authenticationProcessingDetailspour établir si le MFA a été satisfait par une clé FIDO2, une notification push, ou contourné par une liste blanche d’adresses IP. - Détecter le vol de session AiTM : identifier une connexion interactive sur une IP A (proxy de phishing), suivie d’appels API Graph non interactifs sur une IP B (infrastructure de l’attaquant) avec le même identifiant de session.
- Isoler les attaques sur protocoles obsolètes : mettre en lumière les attaques de force brute ciblant l’authentification basique (IMAP, POP3, SMTP) qui échappent aux règles MFA modernes.
Ce qui n’est pas possible
Section intitulée « Ce qui n’est pas possible »- Observer les actions sur les données : les sign-in logs indiquent qu’un jeton a été émis pour SharePoint ; ils n’indiquent aucunement quels fichiers ont été téléchargés.
- Récupérer l’historique au-delà de 7 jours sur entra free : sur un tenant sans licence P1/P2, les enregistrements antérieurs à 7 jours sont irrévocablement perdus.
- Tracer les connexions locales hors ligne : l’ouverture de session locale sur un poste Windows avec des identifiants en cache ne génère aucun événement STS cloud.
- Identifier l’attaquant derrière un proxy résidentiel : si l’attaquant loue des adresses IP résidentielles légitimes, les logs n’affichent que l’adresse IP du fournisseur d’accès du particulier relais ; remonter à l’attaquant exige des réquisitions judiciaires.
Méthodologie d’investigation : extraction et détection KQL
Section intitulée « Méthodologie d’investigation : extraction et détection KQL »# ==============================================================================# Hermes Codex - Extracteur des journaux de connexion Entra ID# Exporte les connexions interactives et non interactives avec pagination# ==============================================================================
Import-Module Microsoft.Graph.Authentication, Microsoft.Graph.Reports -ErrorAction StopConnect-MgGraph -Scopes "AuditLog.Read.All", "Directory.Read.All" -NoWelcome
$TargetUser = "victime@domaine.com"$DaysBack = 14$StartDate = (Get-Date).AddDays(-$DaysBack).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")$OutDir = "./Entra_SignIns_$(Get-Date -Format 'yyyyMMdd')"New-Item -ItemType Directory -Path $OutDir -Force | Out-Null
# 1. Extraire les connexions interactivesWrite-Host "[*] Extraction des connexions interactives pour $TargetUser..." -ForegroundColor Cyan$filter = "userPrincipalName eq '$TargetUser' and createdDateTime ge $StartDate"$interactiveUri = "https://graph.microsoft.com/v1.0/auditLogs/signIns?`$filter=$filter&`$top=500"
$allSignIns = [System.Collections.Generic.List[PSObject]]::new()do { $resp = Invoke-MgGraphRequest -Method GET -Uri $interactiveUri if ($resp.value) { $allSignIns.AddRange($resp.value) } $interactiveUri = $resp.'@odata.nextLink' Start-Sleep -Milliseconds 150} while ($null -ne $interactiveUri)
$interactiveFile = "$OutDir/SignIns_Interactifs.jsonl"$allSignIns | ForEach-Object { $_ | ConvertTo-Json -Compress -Depth 10 } | Set-Content -Path $interactiveFileWrite-Host "[+] $interactiveFile sauvegardé ($($allSignIns.Count) événements)." -ForegroundColor Green
# 2. Calcul du hash SHA-256$hash = Get-FileHash -Path $interactiveFile -Algorithm SHA256Write-Host "[✓] Empreinte SHA-256 : $($hash.Hash)" -ForegroundColor Green// ==============================================================================// Détection de réutilisation de session AiTM (Saut d'IP / ASN au sein d'une session)// Relie une connexion interactive à une activité non interactive depuis une nouvelle IP// ==============================================================================let FenetreRecherche = 24h;
// Étape 1 : Identifier les connexions interactives réussieslet ConnexionsInteractives = SigninLogs| where TimeGenerated >= ago(FenetreRecherche)| where ResultType == 0| project InteractiveTime = TimeGenerated, UserPrincipalName, InteractiveIP = IPAddress, InteractiveCity = LocationDetails.city, InteractiveASN = AutonomousSystemNumber, AppDisplayName, CorrelationId;
// Étape 2 : Corréler avec les rédemptions de jetons non interactivesAADNonInteractiveUserSignInLogs| where TimeGenerated >= ago(FenetreRecherche)| where ResultType == 0| project NonInteractiveTime = TimeGenerated, UserPrincipalName, NonInteractiveIP = IPAddress, NonInteractiveCity = LocationDetails.city, NonInteractiveASN = AutonomousSystemNumber, NonInteractiveApp = AppDisplayName, CorrelationId| join kind=inner (ConnexionsInteractives) on UserPrincipalName| where NonInteractiveTime between (InteractiveTime .. (InteractiveTime + 2h))| where NonInteractiveIP != InteractiveIP| where NonInteractiveASN != InteractiveASN| project NonInteractiveTime, UserPrincipalName, InteractiveIP, NonInteractiveIP, InteractiveCity, NonInteractiveCity, NonInteractiveApp, CorrelationId| order by NonInteractiveTime desc// ==============================================================================// Détection d'une attaque par pulvérisation de mots de passe (Password Spray)// ==============================================================================let FenetreSpray = 1h;let SeuilComptes = 10; // Nombre minimal de comptes ciblés par IP
SigninLogs| where TimeGenerated >= ago(FenetreSpray)| where ResultType in (50126, 50053) // Mauvais mot de passe ou compte verrouillé| summarize NombreComptesCibles = dcount(UserPrincipalName), EchantillonComptes = make_set(UserPrincipalName, 10), TotalTentatives = count() by IPAddress, tostring(LocationDetails.countryOrRegion), AppDisplayName, bin(TimeGenerated, 15m)| where NombreComptesCibles >= SeuilComptes| order by NombreComptesCibles descExemple d’investigation : le vol de session en 4 minutes
Section intitulée « Exemple d’investigation : le vol de session en 4 minutes »Déroulement chronologique
Section intitulée « Déroulement chronologique »-
10H14:02 UTC - authentification interactive via phishing :
- Flux :
interactiveUser(dansSigninLogs). UserPrincipalName:daf@entreprise.fr.IPAddress:185.220.101.5(nœud de sortie Tor hébergeant un reverse proxy Evilginx).ResultType:0(Succès).ClientAppUsed:Browser(Chrome).AuthenticationDetails: mot de passe validé + notification push approuvée sur Microsoft Authenticator.- Fait forensique : l’utilisateur a saisi ses identifiants sur le proxy de l’attaquant, qui a capturé les cookies de session
ESTSAUTHetESTSAUTHPERSISTENT.
- Flux :
-
10H18:14 UTC - réutilisation du cookie par l’attaquant :
- Flux :
nonInteractiveUser(dansAADNonInteractiveUserSignInLogs). UserPrincipalName:daf@entreprise.fr.IPAddress:91.240.118.12(hébergeur en Europe de l’Est).ResultType:0(Succès).AppDisplayName:Microsoft Graph PowerShell.AuthenticationProcessingDetails:IsPRT: False,TokenIssuerType: AzureAD.ConditionalAccessStatus:success(MFA hérité de la session légitime).- Fait forensique : l’attaquant a importé le cookie volé dans un navigateur automatisé et a immédiatement requis un jeton d’accès pour PowerShell Graph sans déclencher de second challenge MFA.
- Flux :
Doctrine transversale : télémétrie de connexion vs preuve malveillante
Section intitulée « Doctrine transversale : télémétrie de connexion vs preuve malveillante »L’analyse d’un log de connexion exige le respect strict de la gradation des niveaux de certitude :
+-------------------------------------------------------------------------------+| LES 7 NIVEAUX DE CERTITUDE FORENSIQUE || || 1. Possible -> Le compte existe et l'annuaire autorise l'authentification.|| 2. Configuré -> L'accès conditionnel et l'enrôlement MFA sont en place. || 3. Autorisé -> Les droits du compte permettent l'accès à l'application. || 4. Accessible -> L'accès réseau et les listes blanches autorisent le flux. || 5. Utilisé -> Des identifiants ont été soumis et traités par le STS. || 6. Observé -> Une ligne apparaît dans SigninLogs ou NonInteractive. || 7. Prouvé -> L'usurpation est prouvée par la discordance IP/ASN et l'UAL.|+-------------------------------------------------------------------------------+Applications directes aux journaux de connexion
Section intitulée « Applications directes aux journaux de connexion »- Autorisé != utilisé : un compte d’administrateur était autorisé à se connecter au portail Azure. Tant qu’aucune ligne avec l’
AppId = "c44b3eab-8279-4727-b0e6-c558e6fbda3a"n’est observée dansSigninLogs, la connexion n’a pas eu lieu. - Utilisé != observé : un attaquant mène une attaque par force brute sur un tenant Entra Free, mais l’analyse débute à J+12. L’attaque a été utilisée, mais elle n’est plus observée en raison de l’écrasement des logs après 7 jours.
- Observé != prouvé : observer une connexion réussie (
ResultType = 0) depuis une IP inhabituelle atteste d’un événement d’authentification observé. Pour prouver qu’il s’agit d’un piratage, l’analyste doit démontrer l’impossibilité physique du voyage (ex. badgeage physique en entreprise au même instant) ou corréler la session avec des actions destructrices observées dans l’UAL (ex. création de règles de suppression d’e-mails).
Pièges et confusions fréquentes
Section intitulée « Pièges et confusions fréquentes »| Piège | Cause technique | Conséquence pour l’enquête | Action corrective |
|---|---|---|---|
| Ignorer les logs non interactifs | Se focaliser uniquement sur SignInLogs et négliger AADNonInteractiveUserSignInLogs. | 90 % des vols de jetons, rejeux de session et exfiltrations API restent invisibles. | Requêter simultanément les tables interactives et non interactives. |
| Faire une confiance aveugle à la géolocalisation IP | Les bases GeoIP attribuent parfois une mauvaise localisation aux VPN et connexions 4G/5G. | Fausses alertes d’intrusion étrangère pour un collaborateur en déplacement ou en télétravail. | Corréler systématiquement la ville avec le numéro d’ASN et la réputation de l’IP. |
| Confondre erreur 50126 et attaque ciblée | L’erreur 50126 (mauvais mot de passe) survient des millions de fois par jour par simple faute de frappe. | Mobilisation d’analystes sur du bruit de fond d’Internet sans portée réelle. | Rechercher les anomalies de volume, les User-Agents rares ou les attaques distribuées sur plusieurs comptes. |
| Croire que le MFA immunise contre la compromission | Les proxys AiTM relaient en direct les notifications push que les victimes valident légitimement. | Conclure à tort à l’absence de compromission car le statut MFA est marqué « Success ». | Analyser la continuité de la session : comparer l’IP interactive avec les IP non interactives suivantes. |
| Sur-interpréter les rotations d’adresses IPv6 | Les smartphones changent fréquemment d’adresse IPv6 au gré des antennes relais. | Multiples alertes d’impossible travel injustifiées. | Vérifier la conformité de l’appareil (deviceId, isManaged) au sein des sous-réseaux IPv6. |
État de la fonctionnalité en 2026
Section intitulée « État de la fonctionnalité en 2026 »Changements récents
Section intitulée « Changements récents »- Parité stricte des schémas graph et log analytics : les tables Log Analytics intègrent la totalité des champs du point de terminaison Graph v1.0
/auditLogs/signIns. - Marquage global Secure access (GSA) : le champ
networkLocationDetailspermet de différencier instantanément le trafic Internet direct du trafic routé par le Security Service Edge (SSE) de Microsoft. - Télémétrie token protection : lorsque la protection des jetons liés aux appareils (Device-Bound Tokens) est active, les journaux consignent les preuves cryptographiques de possession dans
authenticationProcessingDetails.
Fonctionnalités dépréciées
Section intitulée « Fonctionnalités dépréciées »- API azure AD graph (
graph.windows.net) : définitivement désactivée. Tous les scripts de collecte doivent obligatoirement utiliser Microsoft Graph. - Authentification basique Exchange ActiveSync : bloquée sans exception au niveau du tenant. Les tentatives résiduelles génèrent un rejet immédiat.
Limitations actuelles
Section intitulée « Limitations actuelles »- Latence d’ingestion sur grands tenants : sur les environnements de plus de 50 000 utilisateurs, l’apparition des logs non interactifs dans Log Analytics peut accuser un retard de 15 à 20 minutes.
- Champs JSON imbriqués en KQL : plusieurs sous-objets (
DeviceDetail,AuthenticationProcessingDetails) sont stockés sous forme de chaînes dynamiques nécessitant un transtypage explicite viatostring()en KQL.
Points clés à retenir
Section intitulée « Points clés à retenir »- La criminalité cloud réside dans les logs non interactifs : le vol de jetons, les rejeux de sessions AiTM et les exfiltrations via API s’observent dans
AADNonInteractiveUserSignInLogs. - Connaître les codes d’erreur clés : identifier immédiatement
50126(mot de passe invalide),50053(compte verrouillé),50074(MFA requis) et53003(blocage CA). - Pivoter via CorrelationId : utiliser l’identifiant de corrélation de la connexion pour relier l’authentification aux actions Purview UAL sur les boîtes mail et fichiers.
- Se méfier du succès MFA : un MFA réussi n’exclut pas une attaque AiTM ; contrôler la concordance d’IP et d’ASN entre l’accès interactif et les accès non interactifs ultérieurs.
- Activer l’archivage externe d’urgence : les tenants Entra Free écrasent les logs après 7 jours ; diffuser immédiatement vers Log Analytics pour préserver les preuves.