Back to Checks
m365

Conditional Access policy enforces application restrictions for unmanaged devices

entra_conditional_access_policy_app_enforced_restrictions
Service: entraSeverity: medium
OfficialBuilt-inVerified
Categories:e3
Resource Type:NotDefined

Microsoft Entra Conditional Access policy with application enforced restrictions limits access to SharePoint, OneDrive, and Exchange content from unmanaged devices.

This control helps prevent data exfiltration by restricting download, print, and sync capabilities on devices that are not managed by the organization.

Prowler logoProwlerprowler-provider-m365

Risk

Without application enforced restrictions, users accessing SharePoint, OneDrive, and Exchange from unmanaged devices can:

  • Download sensitive files to personal devices
  • Print confidential documents
  • Sync corporate data to uncontrolled locations

This increases the risk of data leakage and unauthorized access to sensitive information.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler m365 --checks entra_conditional_access_policy_app_enforced_restrictions

Recommendation

Configure Conditional Access policies with application enforced restrictions to control access from unmanaged devices. Apply this to Office 365 applications (SharePoint, OneDrive, Exchange) to limit download, print, and sync operations.

Combine with SharePoint access control settings for comprehensive protection.

Remediation

Other

1Navigate to the Microsoft Entra admin center https://entra.microsoft.com.
2Expand **Protection** > **Conditional Access** and select **Policies**.
3Click **New policy**.
4Under **Users**, select **All users**.
5Under **Target resources**, select **Office 365** from the cloud apps.
6Under **Conditions** > **Client apps**, select **All client apps**.
7Under **Session**, check **Use app enforced restrictions**.
8Set 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 (
    ClientAppType,
    ConditionalAccessPolicyState,
)


class entra_conditional_access_policy_app_enforced_restrictions(Check):
    """Check if at least one Conditional Access policy enforces application restrictions.

    This check verifies that the tenant has at least one enabled Conditional Access policy
    with application enforced restrictions to protect SharePoint, OneDrive, and Exchange
    from unmanaged devices.

    - PASS: At least one policy is enabled with application enforced restrictions targeting
            all users, all client app types, and either the Office365 suite or
            SharePoint Online and Exchange Online individually.
    - FAIL: No policy meets the criteria for application enforced restrictions.
    """

    # SharePoint Online / OneDrive for Business
    SHAREPOINT_APP_ID = "00000003-0000-0ff1-ce00-000000000000"
    # Exchange Online
    EXCHANGE_APP_ID = "00000002-0000-0ff1-ce00-000000000000"
    # Office 365 suite (includes SharePoint, OneDrive, and Exchange)
    OFFICE365_APP_ID = "Office365"

    REQUIRED_APPS = {SHAREPOINT_APP_ID, EXCHANGE_APP_ID}
    MODERN_CLIENT_APP_TYPES = {
        ClientAppType.BROWSER,
        ClientAppType.MOBILE_APPS_AND_DESKTOP_CLIENTS,
    }

    def _targets_all_client_apps(self, client_app_types: list[ClientAppType]) -> bool:
        """Check if the policy targets all modern client app types.

        Returns True if the policy includes ALL explicitly or both
        Browser and Mobile apps and desktop clients.
        """
        client_app_set = set(client_app_types)
        if ClientAppType.ALL in client_app_set:
            return True
        return self.MODERN_CLIENT_APP_TYPES.issubset(client_app_set)

    def _targets_required_apps(self, included_applications: list[str]) -> bool:
        """Check if the policy targets the required applications.

        Returns True if the policy includes Office365 (the suite) or both
        SharePoint Online and Exchange Online individually.
        """
        if self.OFFICE365_APP_ID in included_applications:
            return True
        return self.REQUIRED_APPS.issubset(set(included_applications))

    def execute(self) -> list[CheckReportM365]:
        """Execute the check for application enforced restrictions in Conditional Access policies.

        Returns:
            list[CheckReportM365]: A list containing the result 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 enforces application restrictions for unmanaged devices."

        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 not self._targets_all_client_apps(policy.conditions.client_app_types):
                continue

            if not self._targets_required_apps(
                policy.conditions.application_conditions.included_applications
            ):
                continue

            if (
                not policy.session_controls.application_enforced_restrictions
                or not policy.session_controls.application_enforced_restrictions.is_enabled
            ):
                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} reports application enforced restrictions but does not enforce them."
            else:
                report.status = "PASS"
                report.status_extended = f"Conditional Access Policy {policy.display_name} enforces application restrictions for unmanaged devices."
                break

        findings.append(report)
        return findings