At least one Conditional Access policy blocks legacy authentication
Microsoft Entra Conditional Access has an active policy that blocks legacy authentication for All users and All cloud apps by targeting legacy client app types (e.g., Exchange ActiveSync, other basic-auth clients) and enforcing Block access.
Risk
Allowing legacy authentication enables password spray and credential stuffing that bypass MFA, leading to account takeover. Compromised sessions threaten confidentiality (mail, files), integrity (settings, data changes), and availability, and enable lateral movement across Microsoft 365.
Run this check with Prowler
Run in Prowler Cloudprowler m365 --checks entra_legacy_authentication_blockedRecommendation
Enforce a tenant-wide policy to block legacy authentication for All users and All cloud apps, targeting legacy client app types. Migrate apps and devices to modern authentication. Keep minimal, monitored exclusions for break-glass/service accounts, prefer managed identities, and apply zero trust and least privilege.
Remediation
CLI
az rest --method post --url https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies --body '{"displayName":"<example_policy_name>","state":"enabled","conditions":{"users":{"includeUsers":["All"]},"applications":{"includeApplications":["All"]},"clientAppTypes":["exchangeActiveSync","other"]},"grantControls":{"builtInControls":["block"],"operator":"OR"}}'Terraform
resource "azuread_conditional_access_policy" "<example_resource_name>" {
display_name = "<example_resource_name>"
state = "enabled" # critical: enforce the policy
conditions {
users {
include_users = ["All"] # critical: include all users
}
applications {
include_applications = ["All"] # critical: include all cloud apps
}
client_app_types = ["exchangeActiveSync", "other"] # critical: target legacy auth clients
}
grant_controls {
built_in_controls = ["block"] # critical: block access
}
}Other
Check Code
from prowler.lib.check.models import Check, CheckReportM365
from prowler.providers.m365.services.entra.entra_client import entra_client
from prowler.providers.m365.services.entra.entra_service import (
ClientAppType,
ConditionalAccessGrantControl,
ConditionalAccessPolicyState,
)
class entra_legacy_authentication_blocked(Check):
"""Check if at least one Conditional Access policy blocks legacy authentication.
This check ensures that at least one Conditional Access policy blocks legacy authentication.
"""
def execute(self) -> list[CheckReportM365]:
"""Execute the check to ensure that at least one Conditional Access policy blocks legacy authentication.
Returns:
list[CheckReportM365]: A list containing the results of the check.
"""
findings = []
report = CheckReportM365(
metadata=self.metadata(),
resource={},
resource_name="Conditional Access Policies",
resource_id="conditionalAccessPolicies",
)
report.status = "FAIL"
report.status_extended = (
"No Conditional Access Policy blocks legacy authentication."
)
for policy in entra_client.conditional_access_policies.values():
if policy.state == ConditionalAccessPolicyState.DISABLED:
continue
if "All" not in policy.conditions.user_conditions.included_users:
continue
if (
"All"
not in policy.conditions.application_conditions.included_applications
):
continue
if (
ClientAppType.EXCHANGE_ACTIVE_SYNC
not in policy.conditions.client_app_types
or ClientAppType.OTHER_CLIENTS not in policy.conditions.client_app_types
):
continue
if (
ConditionalAccessGrantControl.BLOCK
in policy.grant_controls.built_in_controls
):
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.display_name,
resource_id=policy.id,
)
if policy.state == ConditionalAccessPolicyState.ENABLED_FOR_REPORTING:
report.status = "FAIL"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' reports legacy authentication but does not block it."
else:
report.status = "PASS"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' blocks legacy authentication."
break
findings.append(report)
return findings