If your primary defense against identity compromise is a threshold that locks an account after five failed attempts, you haven’t stopped brute-force attacks—you’ve just dictated their pacing.
For two decades, identity systems have relied on per-account lockout policies as a core control. Lock out the user after $N$ failed attempts within $M$ minutes, and the attacker goes away. That logic held when adversaries targeted single high-value accounts with vertical brute-force attacks, throwing thousands of permutations against a single admin or jdoe account until the log pipe burst.
Against modern authentication endpoints—whether Microsoft Entra ID, Okta, or on-prem Active Directory—vertical brute force is effectively dead. But password spraying isn’t just a workaround for lockout policies; it is mathematically optimized to exploit the exact blind spot those policies create.
The Math of the Horizontal Blind Spot
To understand why password spraying works so reliably, look at the geometry of the attack. Vertical brute force attempts many passwords against one account. Password spraying flips the matrix: it attempts one weak password against many accounts.
Consider an enterprise tenant with $U = 10,000$ user accounts. The active security policy enforces a standard lockout threshold: $T = 5$ failed attempts within a window of $W = 15$ minutes.
If an attacker launches a vertical attack against a single account trying 1,000 passwords, the math is simple: $$\text{Attempts Allowed Before Lockout} = T - 1 = 4$$ The 5th attempt triggers a lockout. The attack fails after testing 0.5% of the dictionary, generates a security alert, and prompts a password reset for that user.
Now consider the horizontal attack. The attacker curates a targeted dictionary of 10 high-probability passwords derived from seasonal trends, company location, and local sports teams (e.g., Autumn2024!, Company123!).
Instead of exhausting $T$ on one user, the attacker distributes the attempts across all $U$ users, maintaining a pacing interval $P$ between attempts to the same account: $$P > W$$
If $P = 30 \text{ minutes}$, the per-account failure count for every single user in the directory remains strictly at 1 attempt per window. From the perspective of any individual user’s account lockout counter: $$\text{Count}(Failures_{\text{user}}) = 1 \ll T$$
The account lockout logic never increments past 1. It resets to 0 at the end of every window $W$. Yet across the entire enterprise tenant, the attacker executes: $$\text{Total Attempts} = 10,000 \text{ users} \times 10 \text{ passwords} = 100,000 \text{ attempts}$$
In an enterprise where 0.5% of users inevitably choose predictable, policy-compliant passwords, this attack yields roughly 50 fully compromised accounts without triggering a single lockout event or per-user alert.
Lockout Policies Actively Help the Attacker
Per-account lockout policies do not merely fail to stop password spraying; they actually assist the attacker in three distinct ways.
First, lockouts serve as an oracle for account existence and state. When an attacker is enumerating a tenant, a lockout response explicitly confirms that an account exists, is active, and is subject to policy.
Second, aggressive lockout policies create a low-cost Operational Denial of Service (DoS) vector. An attacker who wants to disrupt a SOC or lock out critical infrastructure operators doesn’t need to crack a password; they just need to feed known usernames into a script with bad passwords. Because security teams dread self-inflicted outages, they often relax lockout thresholds or extend lock windows—giving the password sprayer even more operational margin.
Third, and most critically, lockout policies force attackers to adopt clean execution discipline. A naive attacker gets caught because they are loud. A sophisticated attacker uses residential proxy networks (RPNs) to pair every request with a distinct IP address, spreading 100,000 attempts across thousands of egress nodes over a 72-hour period. The presence of a per-account lockout policy forces the adversary to operate at a rate that blends seamlessly into normal background noise—human typos, expired mobile device tokens, and misconfigured mail clients.
The Traditional SIEM Alerting Trap
Most Security Operations Centers (SOCs) monitor authentication logs using rules built around the same flawed logic as the lockout policies themselves.
A standard legacy SIEM detection rule looks like this:
- Condition: Event ID 4625 (Windows) or ResultType 50126 (Entra ID)
- Aggregation:
GROUP BY TargetUser - Threshold:
COUNT > 10within 10 minutes
This rule is a copy of the on-host lockout policy implemented in SIEM logic. It suffers from the exact same structural limitation. When a password spray occurs, the count of failed logins for any given TargetUser is 1. The alert never triggers.
Security teams often try to fix this by lowering the threshold to 3 or 4 failures. This results in alert fatigue, as normal user misconfigurations—such as a user changing their domain password while their phone attempts to sync email with the old credential—generate hundreds of false positives daily.
Looking at authentication failures through the lens of individual user accounts hides the attack. The signal isn’t in the depth of failures per account; it is in the breadth of failures across the tenant.
The Real Signal: Tenant-Wide Failure Dispersion
To catch a password spray, you must shift your detection axis from vertical failure density to horizontal failure dispersion.
During normal operations, authentication failures in an enterprise follow a Pareto distribution: a small number of specific users (service accounts with expired keys, users with broken mobile apps) account for the vast majority of failed logins. Furthermore, these failures originate from a limited, stable set of IP addresses associated with corporate egress points, trusted VPNs, or known ISP pools for remote workers.
A password spray completely alters the statistical mechanics of authentication failures across the tenant. It introduces two distinct anomalies:
- High Account Dispersion per Source Identity: An anomalous source IP or ASN attempts to authenticate against an unusually high number of distinct user accounts within a defined window, regardless of whether those attempts succeed or fail.
- Elevated Low-Density Failure Rates Across the Tenant: The ratio of unique targeted accounts to total failure volume spikes across the entire tenant, even if no single account crosses a high failure threshold.
Instead of measuring Failures per User, you must measure Unique Target Users per Source Context.
Engineering the Detection Logic
Effective password spray detection requires aggregating log data by source attributes (IP, Subnet, ASN, User-Agent) and calculating the cardinality of targeted accounts.
Here is an example KQL (Kusto Query Language) query for Azure Sentinel / Microsoft Entra ID logs that isolates horizontal spraying by identifying source IPs attempting logins across an abnormal number of distinct accounts:
SigninLogs
| where TimeGenerated > ago(1h)
| where ResultType in (50126, 50053) // Invalid password, Account locked
| summarize
TotalFailedAttempts = count(),
DistinctAccountsTargeted = dcount(UserPrincipalName),
TargetedAccounts = make_set(UserPrincipalName, 10),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by IPAddress, AppDisplayName, UserAgent
| where DistinctAccountsTargeted >= 15
| extend DurationMinutes = datetime_diff('minute', LastSeen, FirstSeen)
| project
IPAddress,
AppDisplayName,
UserAgent,
DistinctAccountsTargeted,
TotalFailedAttempts,
DurationMinutes,
TargetedAccounts
| sort by DistinctAccountsTargeted desc
This logic ignores how many times a single account failed. It triggers specifically when an external entity targets 15 or more unique accounts within an hour.
Handling Distributed Proxy Networks
Sophisticated attackers rotate IP addresses on every request using residential proxy pools. In this scenario, IPAddress aggregation alone will fail because no single IP reaches the DistinctAccountsTargeted threshold.
To defeat IP rotation, group by broader contextual identifiers, such as the Autonomous System Number (ASN) combined with the User-Agent string, or calculate tenant-wide entropy.
When an attacker sprays using a residential proxy network, the total volume of failed logins across the entire tenant may only rise by 5-10%, but the cardinality of failed accounts per unit time increases dramatically.
SigninLogs
| where TimeGenerated > ago(2h)
| where ResultType in (50126, 50053)
| summarize
UniqueUsersWhoFailed = dcount(UserPrincipalName),
TotalFailures = count(),
UniqueIPs = dcount(IPAddress),
UniqueASNs = dcount(AutonomousSystemNumber)
by bin(TimeGenerated, 15m)
| extend FailureToUserRatio = todouble(TotalFailures) / UniqueUsersWhoFailed
// A ratio approaching 1.0 during high failure volumes indicates a horizontal spray
| where UniqueUsersWhoFailed > 50 and FailureToUserRatio < 1.2
In a normal environment, a spike in failures is caused by a few accounts failing repeatedly (meaning TotalFailures is much higher than UniqueUsersWhoFailed, yielding a high ratio). During a distributed spray, almost every failure belongs to a different account, bringing the FailureToUserRatio close to 1.0.
Beyond Lockouts: What Actually Stops the Attack
Once you accept that per-account lockouts do not prevent password spraying, your architectural requirements change. Moving off legacy authentication controls requires three structural interventions:
- Enforce Smart Lockout / Identity Protection Models: Replace static, deterministic lockout thresholds with dynamic risk-based controls. Systems like Entra Smart Lockout analyze behavioral baselines, lockout state based on IP reputation, and allow legitimate users to keep authenticating from familiar locations even if an attacker is spraying their account from a proxy.
- Block Legacy Authentication Protocols: Password sprays heavily target legacy protocols (IMAP, POP3, SMTP, basic Exchange ActiveSync) because these protocols bypass Multi-Factor Authentication (MFA) and do not support modern risk evaluation. Disabling legacy auth forces all traffic through modern endpoints where conditional access policies apply.
- Eliminate Passwords as the Primary Credential: Password spraying only works because passwords exist. Implementing phishing-resistant FIDO2/WebAuthn tokens or certificate-based authentication renders password availability irrelevant. If a user has no password to check, the horizontal math behind the attack collapses entirely.
Per-account lockout policies were designed for a era when attackers ran scripts against individual local accounts over dial-up and T1 lines. Continuing to rely on them as a primary defensive control against modern identity threats isn’t just outdated—it provides a false sense of security while adversaries systematically map and compromise your organization under the noise floor.
Related content
Want a second set of eyes on your security posture?
Let's talk about where your real exposure is.
Book an advisory call