Skip to main content
  1. Tech/

Tech: E-mail Security (SPF, DKIM and DMARC) with Office 365

SPF, DKIM and DMARC are atrocious acronyms for important e-mail security features; and to be clear, this time the goal is not to protect our users from dangerous incoming emails, but to tell our partners’ email systems (receiving our mails) how to distinguish a real e-mail (really originating from our company) from a fake e-mail (sent by an impersonator).

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 entry
  • a:example.com means that our website can send automated mails on our behalf (precisely, the A entry of example.com)
  • include:spf.protection.outlook.com includes the SPF entry of spf.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)

Public/Private key signing diagram, 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.

All of this is done at server level; the recipient’s mail server authenticates the sender’s mail server, at no point do we authenticate the user sending the mail. We have 1-2 key pairs for the whole domain, not per user (like with S/MIME). DKIM is transparent to users; there is typically no visible notification on Outlook.

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 and ruf, like rua=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