Tech: E-mail Security (SPF, DKIM and DMARC) with Office 365
Table of Contents
With SPF, we tell the world which mail servers are allowed to send mails from our domain #
We store somewhere (in the DNS, precisely) the information that “Servers X, Y, Z are legitimate to send mails from [email protected]”. Let’s look at an example, stored as a TXT entry in the DNS:
v=spf1 a:example.com include:spf.protection.outlook.com -all
v=spf1
is the standard beginning of an SPF entrya:example.com
means that our website can send automated mails on our behalf (precisely, the A entry ofexample.com
)include:spf.protection.outlook.com
includes the SPF entry ofspf.protection.outlook.com
(Microsoft’s) into ours; this allows Office 365 to send mails on our behalf-all
means “I reviewed this list and believe it’s accurate”. The alternative is is~all
which means “I don’t know what I’m doing, don’t really trust what I’m saying” and as such, recipients will often ignore the whole SPF. Seriously, don’t use that.
Therefore, in this sample configuration, we allow any Office 365 server - and only Office 365 servers - to send mails from [email protected]. There are good SPF checkers that help validate an SPF entry, especially as the entry is limited in size (lookups).
With DKIM, we digitally sign our outgoing emails #
If you’re not familiar with public key cryptography, let’s just say that you’ll first generate a public and a private key. You can give your public key to the world, and sign documents / messages with the private key. Anyone in possession of your public key can verify that the message has indeed been signed by you, and not by anyone else. Something like this: (courtesy of Wikipedia)
With DKIM, we store a public key in the DNS and get the originating mail server to sign mails with the private key. The recipient’s mail server then uses the public key to verify that each email has actually been signed by a legitimate server in possession of the private key.
What happens if a mail isn’t signed? Well, the recipient’s mail server decides - increase the spam score, or do nothing. That’s where DMARC comes into play. Let’s look into DMARC before coming back to technical details about DKIM.
With DMARC, we tell the recipient’s mail server what to do if an email fails SPF or DKIM #
Having both SPF and DKIM enabled is already a great start to communicate with a mail recipient’s server. It increases trust in legitimate mails, and decreases trust in fake mails (that would be both originating from other servers (SPF) and unsigned (DKIM).
DMARC goes further by telling our recipient’s mail server:
- What to do when an email fails SPF and/or DKIM, by progressively setting
p=none
,p=quarantine
,p=reject
in the configuration - How to contact us when they see a mail that fails SPF and/or DKIM, by setting reporting addresses (
rua
andruf
, likerua=mailto:[email protected]
). Reports must be processed through a dedicated service as it will generate a lot of reports - The percentage of mails to filter with these rules, for slow rollouts
I won’t go further into DMARC here, as there are already great resources online; the main point is to understand its goal.
One more thing - technical details about DKIM #
It’s advised to have multiple public/private keys to simplify key rotation, and when multiple servers send legitimate mails from this domain. That’s why keys have names; each mail contains the name of the key used to generate the signature (selector). Office 365 uses two selectors named selector1
and selector2
; Mailjet uses a single selector called mailjet
. There are two ways to configure DKIM:
- Either by adding the raw key to the DNS, and storing it as a DNS TXT entry (the key is directly in the entry).
- Or by redirecting the request to another DNS entry, usually managed by the mail service provider, through a DNS CNAME entry.
The first case can be illustrated with mailjet’s mailjet
selector. As per their documentation, the configuration requires to create a DNS TXT entry mailjet._domainkey.yourdomain.com
and to paste their public key: (the long string below)
Resolve-DnsName -name "mailjet._domainkey.example.com" -type TXT
Name Type TTL Section Strings (value)
---- ---- --- ------- -------
mailjet._domainkey.example.com TXT 7155 Answer {k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADC
BiQKBgQDKvDbKYT/3XVpcsMtwl2dX7CsiP0SYDVGy
l1DbPfbjtF/aYMWSqUPNcGZtsTvoVl1vP6jV5qHJC
wqCYC4WwhL4TzAot5I5SJHgEsFfl7zgkUqzFHPkVO
+kpTmW8nqENyHs4+L4s0rq6J2xw0ABmZbvh7qgn1r
sLTjyHO+53GkEcwIDAQAB}
The second option (redirection) is illustrated by Microsoft’s selector1
and selector2
selectors, for a domain using Office 365:
Resolve-DnsName -name "selector1._domainkey.example.com" -type CNAME
Name Type TTL Section NameHost (value)
---- ---- --- ------- --------
selector1._domainkey.example.com CNAME 3114 Answer selector1-examplecom-fr02b._domainkey.example.onmicrosoft.com
The DNS entry selector1._domainkey.example.com
(under control of the Office 365 customer) redirects the request to selector1-examplecom-fr02b._domainkey.example.onmicrosoft.com
(under control of Microsoft). The actual key is hosted/provided by Microsoft, to simplify administration (Office 365 admins never have to update the public key, Microsoft takes care of it).
And selector1-examplecom-fr02b._domainkey.example.onmicrosoft.com
contains the actual key as can be seen here:
Resolve-DnsName -name "selector1-examplecom-fr02b._domainkey.example.onmicrosoft.com" -type TXT
Name Type TTL Section Strings (value)
---- ---- --- ------- -------
selector1-examplecom-fr02b._domainkey.example.onmicrosoft.com TXT 3382 Answer {v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQ
UAA4GNADCBiQKBgQC6Ajx3DURj8u6DWs4LB/EFkPd
gSDtEVXJr78v+nRvkWDZS38GC8SRy27BueAmcna6k
jBa8E/DmKG1ChnzUQtYgc8DtF7etAro27GRqz3cuB
gjP7URUTYj5gZXV+HDyhJS0k9mU9NsJEiiwPqMnat
zckwmce/I3/8WQMucNwk8InQIDAQAB;}
Finding out the adequate configuration domain by domain for Office 365 is pretty easy: (we automated that over hundreds of domains)
$domain = "example.com"
# Generate a DKIM Signing configuration (no operational impact)
New-DkimSigningConfig -DomainName $domain
# Get the DKIM Signing configuration (no operational impact)
$dkimConfig = Get-DkimSigningConfig -Identity $domain
Write-Host @"
DKIM status: $($dkimConfig.enabled)"
Create the DNS entry selector1._domainkey.$($domain)
and set its CNAME value to $($dkimConfig.Selector1CNAME)"
Create the DNS entry selector2._domainkey.$($domain)
and set its CNAME value to $($dkimConfig.Selector2CNAME)"
Note: depending on the DNS provider, the domain part ('.$(domain)') may
automatically be added at the end of the entry; don't add it twice.
"@
# Once this is done, enable DKIM - it also has little impact, as Microsoft
# WILL NOT perform the operation until both CNAMEs are properly set.
Set-DkimSigningConfig -Identity $domain -Enabled $true