Back to Checks
m365

Conditional Access policy blocks authentication transfer

entra_conditional_access_policy_authentication_transfer_blocked
Service: entraSeverity: medium
OfficialBuilt-inVerified
Categories:identity-accesse3
Resource Type:NotDefined

At least one enabled Conditional Access policy should target the authentication transfer flow for all users and all resources and set the grant control to Block. Authentication transfer lets a user move an authenticated session from one device to another (for example, by scanning a QR code), which can be abused to move a session onto an attacker-controlled device.

Risk

If authentication transfer is not blocked, an attacker who can present a transfer prompt (e.g., a malicious QR code) may capture an authenticated session and gain access to the victim's resources without needing their credentials or MFA.

Run this check with Prowler

Run in Prowler Cloud
Run
prowler m365 --checks entra_conditional_access_policy_authentication_transfer_blocked

Recommendation

Create a Conditional Access policy that blocks the authentication transfer flow for all users and all 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 **Conditions** > **Authentication flows**, enable and select **Authentication transfer**
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,
    TransferMethod,
)


class entra_conditional_access_policy_authentication_transfer_blocked(Check):
    """Check if at least one Conditional Access policy blocks authentication transfer.

    This check ensures that at least one enabled Conditional Access policy targets
    the authentication transfer flow and blocks access, preventing an authenticated
    session from being seamlessly transferred to another (potentially attacker
    controlled) device.

    - PASS: An enabled Conditional Access policy blocks authentication transfer.
    - FAIL: No Conditional Access policy restricts authentication transfer.
    """

    def execute(self) -> list[CheckReportM365]:
        """Execute the check to verify authentication transfer is blocked by a Conditional Access policy.

        Returns:
            A list of reports containing the result of the check.
        """
        findings = []

        for policy in entra_client.conditional_access_policies.values():
            report = CheckReportM365(
                metadata=self.metadata(),
                resource=policy,
                resource_name=policy.display_name,
                resource_id=policy.id,
            )
            report.status = "FAIL"
            report.status_extended = f"Conditional Access Policy '{policy.display_name}' does not block authentication transfer."

            authentication_flows = policy.conditions.authentication_flows
            blocks_authentication_transfer = (
                policy.state != ConditionalAccessPolicyState.DISABLED
                and "All" in policy.conditions.user_conditions.included_users
                and "All"
                in policy.conditions.application_conditions.included_applications
                and not policy.conditions.application_conditions.excluded_applications
                and authentication_flows
                and TransferMethod.AUTHENTICATION_TRANSFER
                in authentication_flows.transfer_methods
                and ConditionalAccessGrantControl.BLOCK
                in policy.grant_controls.built_in_controls
            )
            if blocks_authentication_transfer:
                if policy.state == ConditionalAccessPolicyState.ENABLED_FOR_REPORTING:
                    report.status = "FAIL"
                    report.status_extended = f"Conditional Access Policy '{policy.display_name}' reports authentication transfer but does not block it."
                else:
                    report.status = "PASS"
                    report.status_extended = f"Conditional Access Policy '{policy.display_name}' blocks authentication transfer."

            findings.append(report)
        return findings