At least one Conditional Access Identity Protection sign-in risk policy protects against high and medium risk sign-ins
Microsoft Entra Conditional Access has a sign-in risk-based Identity Protection policy that targets All users and All cloud apps, evaluates Medium and High sign-in risk, requires MFA, sets sign-in frequency: every time, and is actively enforced not report-only.
Risk
Without this policy, risky authentications using stolen or replayed credentials may proceed without step-up verification, enabling account takeover. Attackers can establish persistent sessions, exfiltrate data, change configurations, and move laterally-eroding confidentiality and integrity and potentially impacting availability through privilege abuse.
Run this check with Prowler
Run in Prowler Cloudprowler m365 --checks entra_identity_protection_sign_in_risk_enabledRecommendation
Adopt a risk-based Conditional Access policy for sign-in risk that applies broadly and enforces MFA with every-time reauthentication for Medium and High risk. Align with Zero Trust and least privilege: ensure MFA enrollment, exclude emergency accounts, validate in report-only, then enforce and regularly review risky sign-in reports.
Remediation
CLI
New-MgIdentityConditionalAccessPolicy -BodyParameter @{displayName='<example_resource_name>';state='enabled';conditions=@{users=@{includeUsers=@('All')};applications=@{includeApplications=@('All')};signInRiskLevels=@('medium','high')};grantControls=@{operator='OR';builtInControls=@('mfa')};sessionControls=@{signInFrequency=@{isEnabled=$true;frequencyInterval='everyTime'}}}Terraform
resource "azuread_conditional_access_policy" "<example_resource_name>" {
display_name = "<example_resource_name>"
state = "enabled" # Critical: enforce policy
conditions {
users {
include_users = ["All"] # Critical: apply to all users
}
applications {
include_applications = ["All"] # Critical: apply to all apps
}
sign_in_risk_levels = ["medium", "high"] # Critical: protect Medium and High sign-in risks
client_app_types = ["all"]
}
grant_controls {
operator = "OR"
built_in_controls = ["mfa"] # Critical: require MFA
}
session_controls {
sign_in_frequency_interval = "everyTime" # Critical: require reauth every time
}
}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 (
ConditionalAccessGrantControl,
ConditionalAccessPolicyState,
RiskLevel,
SignInFrequencyInterval,
)
class entra_identity_protection_sign_in_risk_enabled(Check):
"""Check if at least one Conditional Access policy is an Identity Protection sign-in risk policy.
This check ensures that at least one Conditional Access policy is an Identity Protection sign-in risk policy.
"""
def execute(self) -> list[CheckReportM365]:
"""Execute the check to ensure that at least one Conditional Access policy is an Identity Protection sign-in risk policy.
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 is a sign-in risk based Identity Protection Policy."
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 (
ConditionalAccessGrantControl.MFA
not in policy.grant_controls.built_in_controls
):
continue
if (
SignInFrequencyInterval.EVERY_TIME
!= policy.session_controls.sign_in_frequency.interval
):
continue
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.display_name,
resource_id=policy.id,
)
if (
RiskLevel.HIGH not in policy.conditions.sign_in_risk_levels
or RiskLevel.MEDIUM not in policy.conditions.sign_in_risk_levels
):
report.status = "FAIL"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' is a sign-in risk based Identity Protection Policy but does not protect against high and medium sign-in risk attempts."
elif policy.state == ConditionalAccessPolicyState.ENABLED_FOR_REPORTING:
report.status = "FAIL"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' is a sign-in risk based Identity Protection Policy and reports high and medium risk potential sign-in attempts, but does not protect against them."
else:
report.status = "PASS"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' is a sign-in risk based Identity Protection Policy and does protect against high and medium risk potential sign-in attempts."
break
findings.append(report)
return findings