Microsoft 365 Security

Tuning Defender for Office 365: Safe Links, Safe Attachments, and Anti-Phishing Policies for Low False Positives

Defender for Office 365 ships with sensible defaults, but the gap between “on” and “tuned” is where help-desk tickets and missed phish both live. This is a step-by-step playbook for layering Safe Attachments, Safe Links, and impersonation-aware anti-phishing so coverage stays high while false positives stay rare.

Everything below is driven from Exchange Online PowerShell (the ExchangePowerShell module, formerly EXO V3). The GUI lives in the Microsoft Defender portal under Email & collaboration > Policies & rules > Threat policies, but PowerShell is the only sane way to keep config in source control and diff it across tenants.

# Connect with the modern module (interactive or app-only)
Install-Module -Name ExchangePowerShell -Scope CurrentUser
Connect-ExchangeOnline -UserPrincipalName admin@contoso.onmicrosoft.com

1. Know what your license actually buys

The single most common mistake I see is teams configuring Plan 2 features on a Plan 1 tenant and wondering why nothing detonates the way the docs describe.

Capability Plan 1 Plan 2
Safe Attachments, Safe Links, anti-phishing (impersonation) Yes Yes
Threat Explorer / real-time detections Real-time detections only Full Threat Explorer
Automated Investigation and Response (AIR) No Yes
Attack simulation training No Yes
Campaign views, advanced hunting integration No Yes

Plan 1 is bundled with Microsoft 365 Business Premium; Plan 2 comes with E5 / the E5 Security add-on. The configuration of the three core policies is identical across plans — what differs is your ability to investigate and automate remediation afterward. Plan the triage workflow in section 8 accordingly.

2. Understand policy precedence before you touch anything

Defender evaluates protection policies by priority order (lower number wins) and a recipient is matched by exactly one policy per type. The flat ordering across all policies of a given type is:

  1. Strict preset security policy (highest priority, always wins where it applies)
  2. Standard preset security policy
  3. Custom policies, in their explicit priority order (0, 1, 2, …)
  4. The Default policy (always last, applies to everyone not matched above)

A user covered by the Strict preset will never be evaluated against your custom Safe Links policy, no matter how its priority is set. Map your population before you build custom policies, or you will spend hours debugging “policy not applying.”

Inspect the live ordering for each policy type:

# Safe Attachments rules in priority order
Get-SafeAttachmentRule | Sort-Object Priority |
  Format-Table Name, Priority, State, SentTo, SentToMemberOf, RecipientDomainIs

# Safe Links rules
Get-SafeLinksRule | Sort-Object Priority |
  Format-Table Name, Priority, State, SentTo, SentToMemberOf

# Anti-phish rules
Get-AntiPhishRule | Sort-Object Priority |
  Format-Table Name, Priority, State, SentTo, SentToMemberOf

Each protection has two objects: a policy (the settings) and a rule (the recipient conditions plus priority). They are linked by name. Preset policies are a special case — they do not appear in Get-SafeAttachmentPolicy and are managed only through the preset cmdlets and the portal.

3. Presets vs custom: pick a strategy

The fastest path to strong, Microsoft-maintained protection is the Standard or Strict preset. Presets auto-update as Microsoft tightens recommended values, and the Strict preset is genuinely strict (more aggressive quarantine, lower impersonation thresholds).

A pragmatic enterprise pattern:

Enable presets and scope them:

# View current preset state
Get-EOPProtectionPolicyRule -State Enabled |
  Format-Table Name, Priority, State
Get-ATPProtectionPolicyRule -State Enabled |
  Format-Table Name, Priority, State

# Scope the Strict preset to specific groups (EOP + ATP rules must both be set)
Set-EOPProtectionPolicyRule -Identity "Strict Preset Security Policy" `
  -SentToMemberOf "Tier0-Admins","Finance-Leadership"
Set-ATPProtectionPolicyRule -Identity "Strict Preset Security Policy" `
  -SentToMemberOf "Tier0-Admins","Finance-Leadership"

Presets cannot have their security settings edited — that is the point. The only knobs are scope (who) and, for impersonation, the protected users/domains and trusted senders. If you need to change a threshold, you need a custom policy.

4. Safe Attachments: detonation, dynamic delivery, and global settings

Safe Attachments detonates attachments in a sandbox before delivery. The friction cost is latency, which DynamicDelivery neutralizes for email: the body is delivered immediately and attachments are reattached once scanning completes.

# Custom Safe Attachments policy with dynamic delivery
New-SafeAttachmentPolicy -Name "SA-Standard" `
  -Enable $true `
  -Action DynamicDelivery `
  -QuarantineTag "AdminOnlyAccessPolicy" `
  -Redirect $false

# Bind it to recipients with a rule (priority 0 = evaluated first among custom)
New-SafeAttachmentRule -Name "SA-Standard" `
  -SafeAttachmentPolicy "SA-Standard" `
  -RecipientDomainIs "contoso.com" `
  -Priority 0

Key choices:

Then turn on the global Safe Attachments settings, which protect SharePoint, OneDrive, and Teams independently of mail flow, plus Safe Documents for Office clients (E5):

# ATP for SPO/ODB/Teams + Safe Docs
Set-AtpPolicyForO365 -EnableATPForSPOTeamsODB $true `
  -EnableSafeDocs $true `
  -AllowSafeDocsOpen $false

AllowSafeDocsOpen $false means a file that fails detonation in Protected View stays blocked. Setting it $true lets users bypass — convenient, and exactly the kind of “small” decision that becomes an incident postmortem.

5. Safe Links: rewriting, click-time protection, and trusted exclusions

Safe Links rewrites URLs through a Microsoft wrapper and re-checks reputation at click time, which catches links weaponized after delivery. The defaults are good; the tuning is mostly about coverage breadth and a minimal trusted-URL list.

New-SafeLinksPolicy -Name "SL-Standard" `
  -EnableSafeLinksForEmail $true `
  -EnableSafeLinksForTeams $true `
  -EnableSafeLinksForOffice $true `
  -ScanUrls $true `
  -DeliverMessageAfterScan $true `
  -DisableUrlRewrite $false `
  -EnableForInternalSenders $true `
  -TrackClicks $true `
  -AllowClickThrough $false `
  -CustomNotificationText "Links in this message are protected. Verify before entering credentials."

New-SafeLinksRule -Name "SL-Standard" `
  -SafeLinksPolicy "SL-Standard" `
  -RecipientDomainIs "contoso.com" `
  -Priority 0

The two settings that matter most for user trust:

For trusted URL exclusions, prefer the tenant-level list over per-policy entries so the exclusion is centralized and auditable. Be surgical with wildcards.

# Per-policy do-not-rewrite list (use sparingly)
Set-SafeLinksPolicy -Identity "SL-Standard" `
  -DoNotRewriteUrls "https://partner.example.com/portal/*"

A bare *.example.com in a Safe Links exclusion turns off click-time protection for every host under that domain, including a compromised subdomain. Scope to the full path you actually trust, and review the list quarterly.

6. Anti-phishing: mailbox intelligence, impersonation, and spoof intelligence

This is where false positives are won or lost. The anti-phish policy has three engines, each with its own threshold and its own allow-list.

New-AntiPhishPolicy -Name "AP-Strict-VIP" `
  -Enabled $true `
  -PhishThresholdLevel 3 `
  -EnableMailboxIntelligence $true `
  -EnableMailboxIntelligenceProtection $true `
  -MailboxIntelligenceProtectionAction Quarantine `
  -EnableTargetedUserProtection $true `
  -TargetedUsersToProtect "CEO;ceo@contoso.com","CFO;cfo@contoso.com" `
  -TargetedUserProtectionAction Quarantine `
  -EnableTargetedDomainsProtection $true `
  -TargetedDomainsToProtect "contoso.com","contoso-legal.com" `
  -TargetedDomainProtectionAction Quarantine `
  -EnableOrganizationDomainsProtection $true `
  -EnableSpoofIntelligence $true `
  -AuthenticationFailAction Quarantine `
  -EnableUnauthenticatedSender $true `
  -EnableViaTag $true `
  -EnableFirstContactSafetyTips $true

New-AntiPhishRule -Name "AP-Strict-VIP" `
  -AntiPhishPolicy "AP-Strict-VIP" `
  -SentToMemberOf "Finance-Leadership","Tier0-Admins" `
  -Priority 0

How the engines differ:

The escape hatch for false positives is the trusted senders/domains exclusion on the policy:

# Exempt known-good senders that trip impersonation
Set-AntiPhishPolicy -Identity "AP-Strict-VIP" `
  -ExcludedSenders "noreply@trusted-saas.com" `
  -ExcludedDomains "trusted-saas.com"

Do not disable spoof intelligence to fix a single false positive. Instead, add the specific spoofed pair to the Tenant Allow/Block List spoof entries (section 7). Disabling the engine removes protection for the whole policy population.

7. Advanced delivery: phishing simulations and SecOps mailboxes

Two legitimate flows must bypass filtering, and they must use the Advanced delivery policy — never a mail-flow rule that skips filtering wholesale (that creates a real bypass attackers can ride).

# SecOps mailbox override
$secops = Get-SecOpsOverridePolicy
if (-not $secops) { New-SecOpsOverridePolicy -Name "SecOps-Override" -SentTo "soc-phish@contoso.com" }

# Phishing simulation override (sending IPs + simulation URLs)
New-PhishSimOverridePolicy -Name "PhishSim-Override"
New-PhishSimOverrideRule -Policy "PhishSim-Override" `
  -SenderIpRanges "203.0.113.0/24" `
  -Domains "phishtraining.example"

The Advanced delivery policy is the correct and supported way to exempt these flows. Using a transport rule with “Set SCL to -1” to whitelist a simulation vendor disables Safe Links/Safe Attachments for that path entirely, and threat actors who learn the vendor’s domains will spoof them.

8. Investigate detonations, verdicts, and the Tenant Allow/Block List

When something is blocked, you need to answer why fast. With Plan 2, Threat Explorer (Defender portal > Email & collaboration > Explorer) is the primary tool: filter by Detection technology (e.g., Safe Attachments file detonation, URL detonation reputation, Impersonation user), pivot to the message, and read the detonation verdict and chain.

From PowerShell you can drive the Tenant Allow/Block List (TABL), which is the supported mechanism for overriding verdicts on URLs, files (by SHA256), domains/senders, and spoof pairs.

# Inspect current entries
Get-TenantAllowBlockListItems -ListType Url
Get-TenantAllowBlockListItems -ListType FileHash
Get-TenantAllowBlockListSpoofItems

# Allow a falsely blocked URL for a bounded window (let expire on its own)
New-TenantAllowBlockListItems -ListType Url `
  -Allow -Entries "https://newsletter.partner.com/track*" `
  -ExpirationDate (Get-Date).AddDays(30) `
  -Notes "FP - partner newsletter, ticket INC-4821"

# Block a known-bad file hash
New-TenantAllowBlockListItems -ListType FileHash `
  -Block -Entries "0123abcd...<sha256>..." -NoExpiration

Prefer admin submissions over directly creating allow entries. When you submit a false positive via New-TenantAllowBlockListItems is the manual route, but submitting through the Submissions portal both creates a time-boxed allow and sends the sample to Microsoft for reclassification — fixing the root cause for everyone, not just masking it in your tenant.

9. Build a false-positive triage workflow

A repeatable loop keeps tuning honest. The flow:

  1. Intake — user reports via the built-in Report button (the Microsoft report-message add-in or built-in Outlook button), routed to your reporting mailbox.
  2. Classify — open the message in Threat Explorer, read the detection technology and verdict.
  3. Submit — file an admin submission (false positive). This auto-creates a bounded TABL allow and requests Microsoft reanalysis.
  4. Tune — if a pattern emerges (e.g., a partner repeatedly impersonation-flagged), add a scoped policy exclusion; if it is one-off, let the submission handle it.
  5. Expire — never use -NoExpiration on allow entries unless you have a recurring review.

Wire the user reporting endpoint and quarantine notifications so users have a self-service path that does not require a ticket:

# Route user reports to a mailbox and to Microsoft
Set-ReportSubmissionPolicy -Identity DefaultReportSubmissionPolicy `
  -EnableReportToMicrosoft $true `
  -ReportJunkToCustomizedAddress $true `
  -ReportNotJunkToCustomizedAddress $true `
  -ReportPhishToCustomizedAddress $true `
  -ReportJunkAddresses "soc-reports@contoso.com" `
  -ReportNotJunkAddresses "soc-reports@contoso.com" `
  -ReportPhishAddresses "soc-reports@contoso.com"

Quarantine policies decide whether users can self-release or must request release. The built-in tags give you the spectrum:

Quarantine policy User experience
AdminOnlyAccessPolicy No user access; admin release only (use for malware/high-confidence phish)
DefaultFullAccessPolicy User can view, release, request release
DefaultFullAccessWithNotificationPolicy Full access plus quarantine notification emails

Map the least dangerous verdicts (spam, bulk) to a notification policy so users self-serve, and reserve admin-only release for malware and high-confidence phish. This is the lever that most reduces help-desk load without lowering protection.

Enterprise scenario

A logistics company rolled the Strict preset to its entire population the week before quarter-end. Within hours, the AP team flooded the SOC: every inbound EDI and carrier-rate confirmation from a long-standing freight partner was landing in quarantine. The partner sent from a bulk relay whose envelope domain (mail.partner-relay.net) failed DMARC alignment against the visible From (@bigfreight.com), so spoof intelligence and unauthenticated-sender flagging (EnableUnauthenticatedSender $true) were correctly firing. The constraint: we could not relax the VIP policy, and we could not wait days for an admin submission to reclassify during a financial close.

The fix was a scoped, time-boxed spoof allow in the Tenant Allow/Block List for the exact spoofed pair — not disabling spoof intelligence, and not an SCL -1 transport rule.

# Allow the specific spoofed pair, bounded so it auto-expires after review
New-TenantAllowBlockListSpoofItems `
  -Action Allow `
  -SpoofType External `
  -SendingInfrastructure "mail.partner-relay.net" `
  -SpoofedUser "bigfreight.com"

# Confirm it landed and is the only entry for that domain
Get-TenantAllowBlockListSpoofItems |
  Where-Object SpoofedUser -eq "bigfreight.com" |
  Format-Table SpoofedUser, SendingInfrastructure, Action, SpoofType

The real fix was durable: we asked the partner to publish us in their SPF and align DMARC. Once mail.partner-relay.net passed alignment, we removed the spoof allow. The lesson — a preset rollout is a population event, and partner mail-auth posture, not your policy, is often the actual root cause.

Verify

Confirm the configuration is live and correctly scoped:

# 1. Confirm presets are enabled and scoped
Get-EOPProtectionPolicyRule | Format-Table Name, State, Priority, SentToMemberOf
Get-ATPProtectionPolicyRule | Format-Table Name, State, Priority, SentToMemberOf

# 2. Confirm custom policies are enabled
Get-SafeAttachmentPolicy | Format-Table Name, Enable, Action, QuarantineTag
Get-SafeLinksPolicy     | Format-Table Name, EnableSafeLinksForEmail, AllowClickThrough, DeliverMessageAfterScan
Get-AntiPhishPolicy     | Format-Table Name, Enabled, PhishThresholdLevel, EnableMailboxIntelligence

# 3. Confirm global ATP + Safe Docs
Get-AtpPolicyForO365 | Format-List EnableATPForSPOTeamsODB, EnableSafeDocs, AllowSafeDocsOpen

# 4. Confirm advanced delivery overrides exist
Get-SecOpsOverridePolicy   | Format-Table Name, SentTo
Get-PhishSimOverrideRule   | Format-Table Name, SenderIpRanges, Domains

# 5. Confirm TABL allow entries are time-boxed (watch for missing ExpirationDate)
Get-TenantAllowBlockListItems -ListType Url |
  Format-Table Value, Action, ExpirationDate

For a true end-to-end test, send the EICAR test string as an attachment to a test mailbox covered by your Safe Attachments policy and confirm it is caught, and use a known Microsoft test URL to confirm Safe Links rewriting in a delivered message’s headers (X-MS-Exchange-Organization- headers will reflect the verdict).

Checklist

Pitfalls

The failure modes are predictable. Whitelisting with transport rules (SCL -1) to fix a single false positive punches a hole through every protection for that path — always use the Tenant Allow/Block List or Advanced delivery instead. Disabling spoof intelligence to silence one alert removes protection for the entire policy population; scope the fix to the specific spoof pair. -NoExpiration allow entries quietly accumulate into a permanent bypass list nobody audits. And over-broad impersonation lists turn the anti-phish engine into a false-positive generator — protect the people who are actually targeted, not the whole org chart.

Treat the three policies as one system. Safe Attachments handles the payload, Safe Links handles the delayed-detonation link, and anti-phishing handles the social-engineering vector. Tuning any one in isolation either leaves a gap or generates noise. Source-control the PowerShell above, diff it on every change, and let admin submissions — not manual allow-lists — drive your tuning.

Defender for Office 365Safe LinksAnti-PhishingPreset PoliciesThreat Explorer

Comments

Keep Reading