In this section

EI4.2 Blocking Legacy Authentication

90-120 minutes · Module 4
What you already know

The previous section designed a defense against a specific attack technique. This section continues with the next attack in the chain, building the layered defense that conditional access makes possible.

The attack: password spray through legacy protocols

MITRE ATT&CK: T1110.003 (Password Spraying), T1078.004 (Cloud Accounts)

Password spray campaigns systematically test common passwords against many accounts. When an attacker targets Entra ID through modern authentication (browser, MSAL), the MFA requirement from Policy F1 stops the attack, even if the password is correct, the attacker cannot complete MFA. But if the attacker targets Entra ID through IMAP or POP3, the authentication flow is password-only. The MFA policy does not apply to legacy protocols because the legacy client cannot present a second factor.

This is not a theoretical gap. Threat intelligence consistently shows that attackers routinely probe IMAP and SMTP endpoints alongside modern authentication endpoints during spray campaigns. Automated tooling (CredMaster, MSOLSpray, older versions of Ruler) targets legacy protocols specifically because they bypass MFA. Microsoft's own telemetry shows that legacy authentication is responsible for a disproportionate share of successful compromises.

The attack in the sign-in logs:

// EI4.2 — Detect password spray through legacy protocols
SigninLogs
| where TimeGenerated > ago(24h)
| where ClientAppUsed in ("Exchange ActiveSync", "IMAP4", "POP3", 
    "Authenticated SMTP", "Other clients")
| summarize 
    Attempts = count(),
    SuccessCount = countif(ResultType == 0),
    FailCount = countif(ResultType != 0),
    DistinctUsers = dcount(UserPrincipalName),
    DistinctIPs = dcount(IPAddress)
    by ClientAppUsed, bin(TimeGenerated, 1h)
| where DistinctUsers > 5 and FailCount > 20
| order by Attempts desc
// Pattern: many users targeted, many failures, few successes
// High DistinctUsers with high FailCount = spray campaign in progress
// ANY SuccessCount > 0 = compromised accounts — investigate immediately

A successful legacy auth sign-in during a spray campaign looks like this in the log: ResultType 0 (success), ClientAppUsed "IMAP4" or "POP3", no MFA in the AuthenticationDetails (legacy protocols skip MFA entirely), and ConditionalAccessStatus either "notApplied" (if no legacy auth blocking policy exists) or "success" (if a grant policy applies but does not block legacy auth).

The defense: Policy F2. Block legacy authentication

This is the policy from the Zero Trust framework in EI3.9, fully specified:

Name: F2: Block Legacy Authentication

Assignments: All Users. Exclude: CA-Exclude-BreakGlass.

Target resources: All cloud apps.

Conditions: Client apps → select "Exchange ActiveSync clients" AND "Other clients." Do NOT select "Browser" or "Mobile apps and desktop clients", these are modern authentication and should not be blocked.

Grant: Block access.

Session: None.

State: Enforcing (after the pre-deployment discovery process below).

The policy logic: any authentication attempt using a legacy protocol is blocked, regardless of whether the password is correct. The attacker never gets a successful response. The policy applies to all users and all applications, there are no application-specific exceptions for legacy auth.

Pre-deployment discovery

Before enforcing the block, identify all active legacy authentication in your tenant. Blocking legacy auth without discovery locks out users and applications that depend on it.

// EI4.2 — Complete legacy authentication inventory
// Run this BEFORE deploying the block policy
SigninLogs
| where TimeGenerated > ago(30d)
| where ClientAppUsed in ("Exchange ActiveSync", "IMAP4", "POP3", 
    "Authenticated SMTP", "Other clients", "Exchange Online PowerShell")
| where ResultType == 0  // Only successful — these are active dependencies
| summarize 
    LastUsed = max(TimeGenerated),
    SignInCount = count(),
    DistinctDays = dcount(bin(TimeGenerated, 1d))
    by UserPrincipalName, ClientAppUsed, AppDisplayName
| order by SignInCount desc
// Every row is an active legacy auth dependency that will break when you block
// Action for each row:
// - User with IMAP4: migrate to Outlook or modern email client
// - User with Authenticated SMTP: migrate app to OAuth SMTP or Graph API
// - User with Exchange ActiveSync: verify client supports modern auth
// - User with Other clients: identify the application and migrate

For each dependency found, determine the migration path:

IMAP/POP3 email clients, upgrade to a modern email client that supports OAuth 2.0 (Outlook desktop, Outlook mobile, Apple Mail with modern auth, Thunderbird 78+ with OAuth). Most IMAP/POP3 usage is from users who set up their email client years ago and never updated.

SMTP Basic Auth for applications, migrate to SMTP AUTH with OAuth 2.0 (supported since 2020) or to the Microsoft Graph Mail.Send API. This is the most common migration challenge because it requires application code changes.

Exchange ActiveSync, modern versions of iOS Mail and Android email clients support modern auth with ActiveSync. Older devices may not. If the device cannot be updated, it must be replaced or the user must switch to Outlook mobile.

Exchange Online PowerShell: the Exchange Online Management module v3+ uses modern authentication natively. If legacy PowerShell scripts use basic auth, update them to use the v3 module with Connect-ExchangeOnline.

The SMTP migration challenge in detail

SMTP Basic Auth is the most common migration blocker because many business applications use it for automated email: monitoring systems sending alerts, CRM platforms sending customer communications, invoice generators emailing documents, and reporting tools distributing scheduled reports. Each application must be individually migrated.

The migration options for SMTP:

Option 1: SMTP AUTH with OAuth 2.0. Exchange Online supports SMTP submission (smtp.office365.com, port 587) with OAuth 2.0 authentication. The application registers as an Azure AD application, obtains an OAuth token using client credentials or authorization code flow, and presents the token instead of a username/password. This is the closest migration path to the existing SMTP workflow: the application still sends via SMTP, but the authentication mechanism changes from basic to OAuth.

Option 2: Microsoft Graph Mail.Send API. The application sends email through the Graph API (POST /users/{id}/sendMail) using an Azure AD application registration with the Mail.Send permission. This is the recommended path for new development because it provides a modern, well-documented API with rich error handling. The application does not use SMTP at all, it calls the REST API directly.

Option 3: SMTP relay through an on-premises SMTP server. If the application cannot be modified to support OAuth, it can send email to an on-premises SMTP relay server (which does not require Entra ID authentication), and the relay forwards to Exchange Online using a connector. This is a workaround, it adds infrastructure complexity, but it allows legacy applications to send email without basic auth to Exchange Online.

Managing exceptions during migration

Some applications cannot be migrated immediately. For these, create a targeted exception:

Create a dedicated service account for the application (not a user account). Add the service account to the "CA-Exclude-LegacyAuthApps" group. Exclude this group from Policy F2. Document the exception with: the application name, the business justification, the migration deadline, and the compensating controls.

Compensating controls for excepted service accounts: restrict the account to specific source IP addresses (using a separate conditional access policy with a named location condition), disable interactive sign-in for the account (the account can only be used for SMTP, not for portal access), monitor the account for anomalous sign-in patterns (any sign-in from an IP outside the expected range is suspicious), and set a hard migration deadline (typically 90 days) after which the exception is revoked regardless.

Never keep the entire tenant on the legacy auth exception because one application needs it. The exception should be as narrow as possible, one service account, restricted to one IP range, with a documented deadline.

Deploy in report-only, then enforce

Deploy the policy in report-only mode first. Run for 14 days (a full two-week cycle captures weekly patterns like automated reports). Analyze the report-only results:

// EI4.2 — Report-only analysis: who would be blocked?
SigninLogs
| where TimeGenerated > ago(14d)
| mv-expand CAPolicy = parse_json(ConditionalAccessPolicies)
| where tostring(CAPolicy.displayName) == "F2: Block Legacy Authentication"
| where tostring(CAPolicy.result) == "reportOnlyFailure"
| summarize 
    WouldBeBlocked = count(),
    LastAttempt = max(TimeGenerated)
    by UserPrincipalName, ClientAppUsed, AppDisplayName
| order by WouldBeBlocked desc
// Everyone in this list would lose access when the policy enforces
// Resolve each entry before promoting to enforcing

When the report-only analysis shows zero legitimate users would be blocked (or all remaining entries are spray attempts from external IPs), promote the policy to enforcing.

Post-deployment verification

After enforcement, verify the policy is blocking legacy auth attempts:

// EI4.2 — Verify legacy auth is blocked after enforcement
SigninLogs
| where TimeGenerated > ago(7d)
| where ClientAppUsed in ("Exchange ActiveSync", "IMAP4", "POP3", 
    "Authenticated SMTP", "Other clients")
| summarize 
    Blocked = countif(ResultType == 53003),
    Succeeded = countif(ResultType == 0),
    OtherFailures = countif(ResultType != 0 and ResultType != 53003)
    by ClientAppUsed
// Blocked should be > 0 (the policy is actively stopping attempts)
// Succeeded MUST be 0 — any successful legacy auth = policy gap
// If Succeeded > 0: investigate which user bypassed the block
//   (likely excluded from the policy or using an untargeted app)
LEGACY AUTH — BEFORE AND AFTER F2 BEFORE F2 Attacker sprays IMAP endpoint Correct password → authentication succeeds No MFA prompt (legacy protocol) Result: full mailbox access ✗ AFTER F2 Attacker sprays IMAP endpoint CA Policy F2 blocks immediately ResultType 53003 (blocked by CA) Result: no access, password not validated ✓ F2 is the single most impactful identity security policy It closes the MFA bypass path that every spray tool exploits Deploy this before any other attack-specific policy
Figure EI4.2 - Blocking Legacy Authentication

Environment: Your M365 developer tenant.

Exercise: Create Policy F2 in your developer tenant:

  1. Navigate to Conditional Access → Policies → New policy
  2. Name: "F2: Block Legacy Authentication"
  3. Assignments: All Users, Exclude: CA-Exclude-BreakGlass
  4. Target resources: All cloud apps
  5. Conditions: Client apps → Select "Exchange ActiveSync clients" and "Other clients"
  6. Grant: Block access
  7. Enable: Report-only first

Run the legacy auth inventory query against your dev tenant. In a new developer tenant, you likely have zero legacy auth usage, so the report-only analysis will show no impact. After 48 hours, promote to enforcing.

Then attempt to connect to your dev tenant's Exchange Online using a legacy protocol, for example, configure Thunderbird with IMAP using basic auth (not OAuth). The connection should fail with an authentication error because F2 blocks it. Check the sign-in log to verify the block: ResultType 53003, the F2 policy showing "failure" in ConditionalAccessPolicies.

Compliance Context

The users who "need" IMAP almost always can migrate to a modern email client. Outlook desktop, Outlook mobile, Apple Mail with modern auth, or Thunderbird with OAuth 2.0 (supported since version 78). The migration takes minutes per user. The risk of keeping legacy auth open, unlimited password spray with no MFA, far outweighs the inconvenience of a client upgrade. If a specific application requires SMTP Basic Auth for automated email sending, that application should be migrated to OAuth-based SMTP or the Graph API. If the migration cannot happen immediately, exclude that specific service account from F2, but document the exception, restrict the account to specific IPs if possible, and set a migration deadline. Never keep legacy auth open for the entire tenant because one application needs it.

The reference above captures the operational configuration. The principle below crystallises the design decision.

Reference. F2: Block Legacy Authentication. Deployment Checklist

Pre-deployment discovery (Week 1): Legacy auth inventory query run: [ ] IMAP/POP3 users identified: [count] SMTP Basic Auth applications identified: [count] ActiveSync legacy devices identified: [count] Migration plan created for each dependency: [ ]

Migration (Weeks 2-3): IMAP/POP3 users migrated to modern clients: [ ] SMTP apps migrated to OAuth or Graph API: [ ] ActiveSync devices verified or replaced: [ ] Remaining exceptions documented with justification: [ ]

Report-only deployment (Week 4): Policy F2 deployed in report-only: [ ] Report-only analysis after 14 days: [ ] Zero legitimate users would be blocked: [ ]

Enforcement (Week 5): Policy promoted to enforcing: [ ] Post-enforcement verification query run: [ ] Succeeded count = 0 for all legacy protocols: [ ] Blocked count > 0 (policy is actively stopping attempts): [ ]

Ongoing monitoring: Weekly: verify zero successful legacy auth sign-ins Any exception: review monthly for migration progress