Back to Checks
Conditional Access policy blocks access from untrusted locations
entra_conditional_access_policy_untrusted_locations_blocked
Service: entraSeverity: medium
OfficialBuilt-inVerified
Categories:identity-accesse3
Resource Type:NotDefined
At least one enabled Conditional Access policy should target all users and all resources, include all network locations, exclude trusted locations, and set the grant control to Block. This blocks access originating from locations that are not explicitly trusted by the organization.
Risk
Without a location-based block policy, users can authenticate from any geography or network, including regions where the organization has no presence, giving attackers operating from those locations a viable path to access resources with stolen credentials.
Run this check with Prowler
Run in Prowler CloudRun
prowler m365 --checks entra_conditional_access_policy_untrusted_locations_blockedRecommendation
Create a Conditional Access policy that blocks access from all locations except trusted named locations, targeting all users and resources, excluding only documented break-glass accounts.
Remediation
Other
1Navigate to the Microsoft Entra admin center at https://entra.microsoft.com/
2Go to **Protection** > **Conditional Access** > **Policies** > **New policy**
3Under **Users**, include **All users** (exclude only documented break-glass accounts)
4Under **Target resources**, select **All resources**
5Under **Network**, include **Any location** and exclude **All trusted locations** (or selected trusted named locations)
6Under **Grant**, select **Block access**
7Set the policy to **On** and click **Create**
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,
)
class entra_conditional_access_policy_untrusted_locations_blocked(Check):
"""Check if a Conditional Access policy blocks access from untrusted locations.
At least one enabled Conditional Access policy should target all users and all
resources, include all network locations, exclude trusted locations, and block
access, so that access from untrusted locations is denied.
- PASS: An enabled Conditional Access policy blocks access from untrusted
locations.
- FAIL: No Conditional Access policy blocks access from untrusted locations.
"""
def _excludes_only_trusted(self, exclude_locations, trusted_location_ids) -> bool:
"""Check whether every excluded location is trusted.
Args:
exclude_locations: Location IDs excluded by the policy.
trusted_location_ids: IDs of collected trusted named locations.
Returns:
True if at least one location is excluded and all exclusions are trusted.
"""
if not exclude_locations:
return False
return all(
location_id == "AllTrusted" or location_id in trusted_location_ids
for location_id in exclude_locations
)
def execute(self) -> list[CheckReportM365]:
"""Execute the untrusted-locations Conditional Access policy check.
Returns:
A list containing the untrusted-locations policy evaluation report.
"""
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 access from untrusted locations."
)
trusted_location_ids = {
location.id
for location in entra_client.named_locations
if location.is_trusted
}
untrusted_location_ids = {
location.id
for location in entra_client.named_locations
if not location.is_trusted
}
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
or policy.conditions.application_conditions.excluded_applications
):
continue
locations = policy.conditions.locations
if not locations:
continue
if "All" not in locations.include_locations and not any(
location_id in untrusted_location_ids
for location_id in locations.include_locations
):
continue
# A trusted-location exclusion must exist so trusted networks keep access.
if not self._excludes_only_trusted(
locations.exclude_locations, trusted_location_ids
):
continue
if (
ConditionalAccessGrantControl.BLOCK
not in policy.grant_controls.built_in_controls
):
continue
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}' blocks untrusted locations but is in report-only mode."
else:
report.status = "PASS"
report.status_extended = f"Conditional Access Policy '{policy.display_name}' blocks access from untrusted locations."
break
findings.append(report)
return findings