☁️Entra IDRisk ProtectionIdentityMonitoringConditional Access

Failed Sign-In Burst Detection Entra ID: Why Identity Protection Won't Catch It

A burst of failed sign-ins against one account often triggers nothing in Entra ID Identity Protection. Here's why, plus the KQL query and Smart Lockout tuning that close the gap.

Younes AZABARBy Younes AZABAR11 min read
Failed Sign-In Burst Detection Entra ID: Why Identity Protection Won't Catch It

Failed Sign-In Burst Detection Entra ID: What It Is

Failed sign-in burst detection Entra ID coverage is thin by design: Microsoft Entra ID Identity Protection ships no native risk detection built to catch a burst of failed sign-ins landing on one account, even though the raw signal — dozens of invalid-password attempts against one account in a short window — sits directly in your sign-in logs. This article covers why Identity Protection's own detections don't fire on a pure failure streak, and how to close the gap with a KQL query, Smart Lockout tuning, and an alert that pages before the tenth guess, not after the first successful one.

A wrong password against Entra ID lands in SigninLogs as error code AADSTS50126, and Microsoft's own reference for that code is blunt about why a single occurrence means nothing: InvalidUserNameOrPassword — "Error validating credentials due to invalid username or password. The user didn't enter the right credentials. Expect to see some number of these errors in your logs due to users making mistakes." It is not the only credential-failure code Microsoft documents — AADSTS50056 is "Invalid or null password: password doesn't exist in the directory for this user" — and a detection that watches only one of them sees part of the burst. A typo is noise. Twenty of them against the same account in five minutes, or the same password guessed across fifty accounts in five minutes, is not — that's targeted brute force in the first case and password spray in the second, and both patterns show up as the same raw signal: a burst of 50126.

That distinction matters for what you link this article to. If you want the attacker's playbook — target enumeration, common password lists, why spraying avoids per-account lockouts — that's Password Spraying: Detection and Prevention for Active Directory and Entra ID. This article is the other half: the raw-telemetry detection engineering that has to exist because Identity Protection's own risk engine leaves this specific signal unflagged.


Why Identity Protection Doesn't Flag It

Microsoft documents every sign-in and user risk detection Identity Protection ships, and none of them is "repeated failed sign-ins against one account." The closest candidates all miss this signal for a specific, documented reason:

DetectionWhy a pure failure burst doesn't trigger it
Password spray"The risk detection is only triggered when an attacker successfully validates a user's password. Unsuccessful spray attempts against your users don't generate a detection." A campaign that never guesses right — the common case against a decent password policy — produces zero risk events.
Malicious IP addressThe closest thing to a failure-volume signal, but it is a verdict on the IP, not on the account being hammered. Microsoft calculates it offline, "based on high failure rates because of invalid credentials received from the IP address or other IP reputation sources", and notes that "in some instances, this detection triggers on previous malicious activity". No threshold is published and none is yours to tune, so nothing about it can be relied on for a specific account under attack.
Atypical travel / Impossible travel / Unfamiliar sign-in propertiesAll three score the properties of sign-in activity against learned history. Atypical travel and Impossible travel each need "two sign-ins originating from geographically distant locations"; Unfamiliar sign-in properties fires when a sign-in carries properties unfamiliar to the user — "IP, ASN, location, device, browser, and tenant IP subnet". None of the three counts failures, so an attacker guessing passwords from an IP and browser the tenant already sees leaves them nothing anomalous to score.
Leaked credentialsFires only on a confirmed match against Microsoft's breach-corpus scanning pipeline. It says nothing about live failure telemetry in your own logs.

The practical result: a targeted brute-force run or an unsuccessful spray wave can sit entirely inside SigninLogs — visible, timestamped, attributable to an IP and a set of accounts — without ever touching RiskState, RiskLevelAggregated, or a single row in the risky users report. If your monitoring stops at Identity Protection's dashboard, this pattern is invisible until it succeeds.


Detection: Build the Query Yourself

Since no riskEventType exists for this pattern, the detection has to be built directly against SigninLogs, the Log Analytics table Entra ID sign-in events stream into once diagnostic export is enabled. The columns that matter:

ColumnWhat it holds
ResultType"Provides the 5-6 digit error code that's generated during a sign-in event. 0 indicates success; other values are failures."
ResultDescription"Provides the error message or the reason for failure for the corresponding sign-in activity."
UserPrincipalNameThe UPN of the user.
IPAddressThe IP address of the client from where the sign-in occurred.
TimeGeneratedEvent timestamp, UTC.

Before picking a threshold, baseline your own tenant. Microsoft's published SigninLogs sample queries include a starting point for this — "Failed Signin reasons" — adapted here to separate the credential-failure codes that matter:

SigninLogs
| where TimeGenerated > ago(7d)
| where ResultType in ("50126", "50056", "50053")
| summarize Count = count() by ResultType, ResultDescription
| order by Count desc

Run that first. If ordinary typo-driven 50126 noise is already in the hundreds per day, a burst threshold in the single digits will page on nothing. With a baseline in hand, two queries cover the two flavors of the same catalogue gap — targeted brute force against one identity, and low-and-slow spray across many:

Burst against a single account:

SigninLogs
| where TimeGenerated > ago(1d)
| where ResultType in ("50126", "50056", "50053")
| summarize FailureCount = count(),
            SourceIPs = dcount(IPAddress),
            LastFailure = max(TimeGenerated)
    by UserPrincipalName, bin(TimeGenerated, 15m)
| where FailureCount >= 10
| order by FailureCount desc

Burst spread across many accounts from one source:

SigninLogs
| where TimeGenerated > ago(1d)
| where ResultType in ("50126", "50056", "50053")
| summarize FailureCount = count(),
            TargetedAccounts = dcount(UserPrincipalName),
            Accounts = make_set(UserPrincipalName, 20)
    by IPAddress, bin(TimeGenerated, 15m)
| where FailureCount >= 10 or TargetedAccounts >= 5
| order by FailureCount desc

Both queries use only documented SigninLogs columns; the 10 and 5 thresholds are illustrative starting points, not Microsoft defaults — size them against the baseline query above, then tighten as false positives drop. Wire either query into a Microsoft Sentinel scheduled analytics rule (or a plain Azure Monitor log alert if you don't run Sentinel) so a burst raises an incident instead of waiting to be found during a manual log review.


Remediation

1. Tighten Smart Lockout instead of assuming the default is enough

Smart Lockout is on for every tenant, but its defaults are tuned for broad usability, not detection:

  • Default lockout threshold is 10 failed attempts (Azure Public tenants; 3 for Azure US Government), after which the account locks for an initial 60 seconds, growing on repeated lockouts.
  • Smart Lockout "uses familiar location versus unfamiliar location to differentiate between a bad actor and the genuine user", and "both unfamiliar and familiar locations have separate lockout counters" — so an attacker signing in from a location the real user has never used burns a counter of its own, tracked independently of the user's own mistakes.
  • Smart Lockout also "tracks the last three bad password hashes to avoid incrementing the lockout counter for the same password". A spray replaying one common password against an account therefore never walks it toward lockout — which is precisely the case your own query has to catch. Microsoft notes this hash tracking "isn't available for customers with pass-through authentication enabled".
  • Customizing the threshold and duration below the default requires Microsoft Entra ID P1 or higher, set under Entra ID > Authentication methods > Password protection.

A lower threshold trades a small amount of user friction for cutting a brute-force campaign off well before your 15-minute detection window would even close. Coordinate the change with any hybrid AD DS lockout policy. For pass-through authentication Microsoft is explicit: the Entra lockout threshold "must be less than the AD DS account lockout threshold", set "so that the AD DS account lockout threshold is at least two or three times greater than the Microsoft Entra lockout threshold" — cloud lockout then absorbs the attack before it reaches on-prem.

2. Turn the KQL query into a standing alert

A query that only runs when someone remembers to paste it into Log Analytics isn't a detection. Save it as a Microsoft Sentinel scheduled analytics rule (or an Azure Monitor log alert) on a 15-minute cadence matching the bin() window, and route it to whoever owns identity incidents. This is the control that actually closes the catalogue gap — Identity Protection stays silent on a pure failure burst, so nothing else will page for you.

3. Layer a sign-in risk-based Conditional Access policy for the cases that do land

None of this replaces risk-based Conditional Access — it complements it. If a burst eventually produces one successful guess, a sign-in risk policy is what stops that session from doing anything. The full walkthrough for building one is in Azure Identity Protection: Blocking Leaked Credentials; if your Conditional Access baseline has other coverage holes beyond risk-based sign-in, check it against Entra ID Conditional Access Gaps.

4. Turn on sign-in log export before you need it

SigninLogs only fills once diagnostic export to Log Analytics is enabled — like any Azure Monitor stream, it captures events going forward, not retroactively. Enable the export under Entra ID > Monitoring & health > Diagnostic settings now, so the history is there the next time you need to run these queries against an incident that's already a week old.


How EtcSec Detects This

EtcSec's Risk Protection checks include this exact raw-telemetry correlation — internally tracked as RISK_FAILED_SIGNIN_BURST, rated critical and mapped to MITRE ATT&CK T1110.003 — because Identity Protection's own risk engine, as documented above, has no detection keyed to failure volume against a single account. The check runs the same two shapes as the queries above, over a fixed 10-minute window: more than 50 credential failures against one user, or more than 50 from one IP across more than 10 distinct users. Those are audit thresholds, set high enough that a finding is an incident rather than a starting point — which is exactly why the KQL above starts lower and asks you to baseline first. It sits alongside the checks that audit what happens once Identity Protection does raise a flag: RISK_SIGNINS_NOT_INVESTIGATED (risky sign-ins with no follow-up) and RISK_NO_AUTOMATED_RESPONSE (risk visible, no automated containment configured), and it feeds directly into whether CA_NO_RISK_BASED_SIGNIN (no sign-in risk-based Conditional Access policy) matters in your environment at all.

For the sibling detectors in the same raw-telemetry family, see Entra Service Principal Sign-In Anomaly Detection and Entra ID AiTM Token Replay, Impossible Travel Detection. For the response-side gap once Identity Protection does fire, see Entra ID Risk Protection: Leaked Credentials, Risky Users Not Remediated.


Primary References

Explore the identity security pages that support this topic