Back to Checks
Defender anti-phishing policy active, quarantines spoofed senders and DMARC reject/quarantine failures, honors DMARC policy, safety tips enabled
defender_antiphishing_policy_configured
Service: defenderSeverity: low
OfficialBuilt-inVerified
Categories:email-securitye5
Resource Type:NotDefined
Microsoft Defender for Office 365 anti-phishing policies are evaluated for custom scoping to users, groups, or domains and precedence over the default, plus key settings: spoof intelligence, DMARC honoring, quarantine actions for spoof/DMARC, safety tips, unauthenticated sender indicators, and policy enablement.
Risk
Missing or lax configuration lets spoofed and impersonated emails reach inboxes. Ignoring DMARC or not using quarantine enables delivery of fraudulent messages, driving credential theft, BEC, and account takeover, compromising data confidentiality and integrity and enabling lateral movement via mailbox rule abuse.
Run this check with Prowler
Run in Prowler CloudRun
prowler m365 --checks defender_antiphishing_policy_configuredRecommendation
Apply defense in depth for email:
- Create high-priority custom policies for sensitive users/groups/domains
- Enable spoof intelligence; honor DMARC (
p=quarantine,p=reject) withquarantineactions - Turn on safety tips and unauthenticated sender tags
- Review policy precedence, scope, and thresholds regularly to minimize false positives
Remediation
Other
1Go to Microsoft 365 Defender: https://security.microsoft.com > Email & collaboration > Policies & rules > Threat policies > Anti-phishing
2Open the Default anti-phishing policy and click Edit
3Spoof settings: ensure Enable spoof intelligence is On and set If the message is detected as spoof by spoof intelligence to Quarantine
4DMARC: turn On Honor DMARC record policy and set both actions to Quarantine:
5- If DMARC policy is p=quarantine: Quarantine
6- If DMARC policy is p=reject: Quarantine
7Safety tips & indicators: turn On Show first contact safety tip, Show (?) for unauthenticated senders for spoof, and Show "via" tag
8Save changes
9If using custom anti-phishing policies, ensure their rule Status is On
Check Code
from typing import List
from prowler.lib.check.models import Check, CheckReportM365
from prowler.providers.m365.services.defender.defender_client import defender_client
class defender_antiphishing_policy_configured(Check):
"""
Check if an anti-phishing policy is established and properly configured in the Defender service.
Attributes:
metadata: Metadata associated with the check (inherited from Check).
"""
def execute(self) -> List[CheckReportM365]:
"""
Execute the check to verify if an anti-phishing policy is established and properly configured.
This method checks the Defender anti-phishing policies to ensure they are configured
according to best practices.
Returns:
List[CheckReportM365]: A list of reports containing the result of the check.
"""
findings = []
if defender_client.antiphishing_policies:
# Only Default Defender Anti-Phishing Policy exists since there are only anti phishing rules when there are custom policies
if not defender_client.antiphishing_rules:
# Get the only policy in the dictionary since there is only the default policy
policy = next(iter(defender_client.antiphishing_policies.values()))
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy.name,
resource_id=policy.name,
)
if self._is_policy_properly_configured(policy):
# Case 1: Default policy exists and is properly configured
report.status = "PASS"
report.status_extended = f"{policy.name} is the only policy and it's properly configured in the default Defender Anti-Phishing Policy."
else:
# Case 5: Default policy exists but is not properly configured
report.status = "FAIL"
report.status_extended = f"{policy.name} is the only policy and it's not properly configured in the default Defender Anti-Phishing Policy."
findings.append(report)
# Multiple Defender Anti-Phishing Policies
else:
default_policy_well_configured = False
for (
policy_name,
policy,
) in defender_client.antiphishing_policies.items():
# Preset security policies are scoped by protection policy rules, not filter rules
if (
not policy.default
and policy.name not in defender_client.antiphishing_rules
):
continue
report = CheckReportM365(
metadata=self.metadata(),
resource=policy,
resource_name=policy_name,
resource_id=policy_name,
)
if policy.default:
if not self._is_policy_properly_configured(policy):
# Case 4: Default policy is not properly configured and there are other policies
report.status = "FAIL"
report.status_extended = f"{policy_name} is not properly configured in the default Defender Anti-Phishing Policy, but could be overridden by another well-configured Custom Policy."
findings.append(report)
else:
# Case 2: Default policy is properly configured and there are other policies
report.status = "PASS"
report.status_extended = f"{policy_name} is properly configured in the default Defender Anti-Phishing Policy, but could be overridden by another bad-configured Custom Policy."
default_policy_well_configured = True
findings.append(report)
else:
if not self._is_policy_properly_configured(policy):
included_resources = []
if defender_client.antiphishing_rules[policy.name].users:
included_resources.append(
f"users: {', '.join(defender_client.antiphishing_rules[policy.name].users)}"
)
if defender_client.antiphishing_rules[policy.name].groups:
included_resources.append(
f"groups: {', '.join(defender_client.antiphishing_rules[policy.name].groups)}"
)
if defender_client.antiphishing_rules[policy.name].domains:
included_resources.append(
f"domains: {', '.join(defender_client.antiphishing_rules[policy.name].domains)}"
)
included_resources_str = "; ".join(included_resources)
# Case 3: Default policy is properly configured but other custom policies are not
if default_policy_well_configured:
report.status = "FAIL"
report.status_extended = (
f"Custom Anti-phishing policy {policy_name} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.antiphishing_rules[policy.name].priority} (0 is the highest). "
"However, the default policy is properly configured, so entities not included by this custom policy could be correctly protected."
)
findings.append(report)
# Case 5: Default policy is not properly configured and other custom policies are not
else:
report.status = "FAIL"
report.status_extended = (
f"Custom Anti-phishing policy {policy_name} is not properly configured and includes {included_resources_str}, "
f"with priority {defender_client.antiphishing_rules[policy.name].priority} (0 is the highest). "
"Also, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
else:
included_resources = []
if defender_client.antiphishing_rules[policy.name].users:
included_resources.append(
f"users: {', '.join(defender_client.antiphishing_rules[policy.name].users)}"
)
if defender_client.antiphishing_rules[policy.name].groups:
included_resources.append(
f"groups: {', '.join(defender_client.antiphishing_rules[policy.name].groups)}"
)
if defender_client.antiphishing_rules[policy.name].domains:
included_resources.append(
f"domains: {', '.join(defender_client.antiphishing_rules[policy.name].domains)}"
)
included_resources_str = "; ".join(included_resources)
# Case 2: Default policy is properly configured and other custom policies are too
if default_policy_well_configured:
report.status = "PASS"
report.status_extended = (
f"Custom Anti-phishing policy {policy_name} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.antiphishing_rules[policy.name].priority} (0 is the highest). "
"Also, the default policy is properly configured, so entities not included by this custom policy could still be correctly protected."
)
findings.append(report)
# Case 6: Default policy is not properly configured but other custom policies are
else:
report.status = "PASS"
report.status_extended = (
f"Custom Anti-phishing policy {policy_name} is properly configured and includes {included_resources_str}, "
f"with priority {defender_client.antiphishing_rules[policy.name].priority} (0 is the highest). "
"However, the default policy is not properly configured, so entities not included by this custom policy could not be correctly protected."
)
findings.append(report)
return findings
def _is_policy_properly_configured(self, policy) -> bool:
"""
Check if a policy is properly configured according to best practices.
Args:
policy: The anti-phishing policy to check.
Returns:
bool: True if the policy is properly configured, False otherwise.
"""
return (
(
policy.default
or defender_client.antiphishing_rules[policy.name].state.lower()
== "enabled"
)
and policy.spoof_intelligence
and policy.spoof_intelligence_action.lower() == "quarantine"
and policy.dmarc_reject_action.lower() == "quarantine"
and policy.dmarc_quarantine_action.lower() == "quarantine"
and policy.safety_tips
and policy.unauthenticated_sender_action
and policy.show_tag
and policy.honor_dmarc_policy
)