Mail server hardening checklist
Check support status before anything else. Hardening an Exchange build that no longer receives security updates is wasted work, so run Get-ExchangeServer | Format-List Name, Edition, AdminDisplayVersion, check the build against Microsoft's published list, and plan the upgrade first if it is out of support.
After that the job splits in two: the server itself (Health Checker, Extended Protection, basic auth blocked, ECP off the internet, TLS fixed on SCHANNEL and .NET together) and the domain's email authentication records (one SPF record, 10 lookups or fewer, DKIM signing, DMARC starting at p=none). Tighten the records only once the reports are clean.
Support status and patching
- Run
Get-ExchangeServer | Format-List Name, Edition, AdminDisplayVersionto get the build number - Check that build against Microsoft's published Exchange build numbers page to confirm it is still supported
- Plan the move to Exchange Server Subscription Edition if the server is on Exchange 2016 or 2019
- Get the server onto a Cumulative Update that still receives Security Updates before applying anything else
- Run the Exchange Server Health Checker script from the CSS-Exchange repository and fix every red flag
- Confirm the Exchange Emergency Mitigation service is running and
MitigationsEnabledis true for both organisation and server
Cut the attack surface
- Enable Extended Protection with
ExchangeExtendedProtectionManagement.ps1from the CSS-Exchange repository - Review connectors with
Get-ReceiveConnector | Format-List Name, Bindings, RemoteIPRanges, PermissionGroups, AuthMechanismand remove wideAnonymousUserspermissions - Restrict
/ecpto internal or management addresses at the reverse proxy or in IIS - Set POP3 and IMAP4 to
Manualand stop them unless an application genuinely needs them - Check per-mailbox protocol access with
Get-CASMailboxand turn off what is unused - Match antivirus exclusions to Microsoft's published list exactly, with no broad drive or path exclusions
Authentication and access
- Create the policy with
New-AuthenticationPolicy -Name "Block Basic Auth" - Apply it to a pilot group with
Set-User -Identity <user> -AuthenticationPolicy "Block Basic Auth"before going wider - Set the organisation default with
Set-OrganizationConfig -DefaultAuthenticationPolicy "Block Basic Auth" - Confirm AMSI integration is active with
Get-SettingOverrideand remove anyBypassBodyoverride - Review
Get-ManagementRoleAssignmentand take Domain Admin accounts out of Exchange administration
TLS and transport
- Disable SSL 2.0, SSL 3.0, TLS 1.0 and TLS 1.1 under
HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols - Enable TLS 1.2 in SCHANNEL and set
SystemDefaultTlsVersionsandSchUseStrongCryptoto1in the .NET Framework keys - Change SCHANNEL and .NET together on every server in the DAG, or mail stops flowing between the ones that disagree
- Test outbound SMTP and Autodiscover with the Remote Connectivity Analyzer at
testconnectivity.microsoft.com - Check certificate expiry with
Get-ExchangeCertificate | Format-List Thumbprint, Services, NotAfter, Subject - Confirm backups are Exchange-aware, restore-tested, and kept where a domain compromise cannot reach them
SPF, DKIM and DMARC
- Publish one DNS TXT record starting with
v=spf1listing every server that sends mail for the domain - Keep the total DNS lookups, including nested
include:entries, at 10 or under to avoid apermerror - Confirm no second SPF record exists, and merge duplicates into a single record
- Remove any
ptrmechanism from the record - Give each sending subdomain its own record, and publish
v=spf1 -allon subdomains that send nothing - Sign outbound mail with DKIM and publish DMARC at
p=nonewith anrua=address before tightening anything
Verify and keep verifying
- End the record with
~alland watch DMARC aggregate reports for a few weeks to catch senders you missed - Tighten to
-alland move DMARC top=quarantine, thenp=reject, once the reports are clean - Validate the live records with the SPF, DKIM and DMARC checker
- Keep an inventory of everything that sends mail as the domain, including MFPs, line-of-business apps and monitoring alerts
- Re-run the Exchange Server Health Checker after every Cumulative Update
- Monitor the mail server itself for patch state and anti-malware health, not only for uptime
Frequently asked questions
What should I check first when hardening a mail server?
Support status. Run Get-ExchangeServer to get the build number and check it against Microsoft's published list. Configuration hardening cannot protect a build that no longer receives security updates, so an unsupported server needs an upgrade plan before anything else is worth doing. The detail is in our Exchange hardening walkthrough.
Why does an SPF record break mail months after it is published?
The 10 DNS lookup limit. Every include: mechanism costs a lookup, and nested includes inside a provider's record count too. A record that was within the limit when published goes over it when a provider adds an include of their own, and the result is a permerror that fails SPF for the whole domain.
Should an SPF record end in -all or ~all?
Start with ~all while DMARC aggregate reports show which legitimate senders you missed, then tighten to -all once the reports are clean. Publishing -all on day one on a domain with an incomplete sender inventory stops real mail. Check what is live with the SPF, DKIM and DMARC checker.
Why must SCHANNEL and .NET TLS settings change together?
Disabling old TLS in SCHANNEL without setting SystemDefaultTlsVersions and SchUseStrongCrypto in the .NET Framework keys leaves Exchange components negotiating a protocol the operating system has switched off. Apply both across every server in a DAG in the same window, or mail stops flowing between the servers that disagree.
A hardened server still goes unpatched if nobody is watching it.
Moorfox tracks patch age, service state and anti-malware health on the mail server alongside every other machine, and alerts when the one server you cannot afford to lose stops checking in.