Skip to main content
  1. Posts/

SPF, DKIM, and DMARC: How to Protect Yourself from People Impersonating You via Email

·8 mins
Ixonae
Networking System Administration
Ixonae
Author
Ixonae
Table of Contents

Nowadays, emails are a wildly used form of communication. Therefore, they are also wildly used as an attack vector. 75% of organizations worldwide are said to have experienced phishing in 2020, and up to 95% of social engineering attacks may be delivered through emails. Of course, there is no magic to prevent all of these attacks from happening, but there are a couple of mechanisms that can help to prevent malicious people from impersonating you via email. These mechanisms only require a few DNS entries to be set (unless you are running your own email server, which we will not cover here). Even better, setting these DNS entries will also improve the delivery of your emails, as they will be less likely to be flagged as spam.

Sender Policy Framework (SPF)
#

Simply put, SPF allows listing servers that are permitted to send emails using a domain name. When an email is sent, the receiving server will query the SPF entry of the sender domain (recovered from the envelope-from) and compare it with the server it is receiving the email from. If this is not matching, the email could be flagged or rejected (ultimately, the receiving server decides what it wants to do).

The configuration is pretty simple, as it just requires you to add a TXT entry to your DNS configuration. For example, the following (content of a DNS TXT entry) allows all the emails coming from 1.1.1.1, 192.168.0.1/8, and the A record of example.com, and says that other servers are not authorized to send emails for this domain name. Note that a rule cannot have more than 10 lookups (e.g., resolving a entries).

v=spf1 ip4:1.1.1.1 ip4:192.168.0.1/8 a:example.com -all

In addition to allowing IP v4 addresses and range, domain names, many other options such as IP v6 and MX are available.

Instead of using -all to have the SPF check fail if emails are not sent from an allowed server, ~all could be used to produce a soft-fail, ?all to state that nothing can be said about the addresses not explicitly marked. +all could also be used to signal that any server is allowed to send emails on the behalf of our domain name

All of this is nice, but SPF is not perfect. Let’s say that you configured your mailbox email@example.com to forward automatically emails to email@example.net. If I send an email from an IP not allowed by SPF to email@example.com, email@example.net will not see that the original SPF is invalid.

Domain Keys Identified Mail (DKIM)
#

We mentioned in the previous part that SPF by itself is not enough to guarantee emails’ authentication for reasons such as being ignored when emails are forwarded. DKIM is another option to authenticate the emails, and it has the advantage of not being lost when emails are forwarded.

The way DKIM works is pretty simple. The domain of the sender needs to have a DNS entry (you will need to refer to your email hosting provider to see how they expect things to be configured) with a public key. The corresponding private key will be used to sign sent emails. When an email is received, the receiving server will pull the public key from the DNS records of the domain name used by the sender, and check that the signature is correct.

Let’s look at an example. The following is part of the header of a received email that was sent from a server using DKIM.

Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
  d=haveibeenpwned.com;
  h=content-type:from:mime-version:to:subject:list-unsubscribe;
  s=s1; bh=XnUR5B4bb9/iGnKkBNkjeCE5H9eTJoZZhuc28eSwj/Y=; b=CwFiOJD
 nrpW8docGIVBd/A+bPcOjmmVg0letY5gf43QQTSD3V1bJ4wkt3l1LSBT1uDqhkzK
 QxBQttzZIxnmcYY5E/sP/tj1UseO0KEBq/s6Mt1X5AHtvDScaIJgoTfeay3sIU+O
 6Edb/G0uCDhSW6JY8gAnXgVKFcooGBp43+yk=

We can see multiple fields:

  • a=rsa-sha256 gives us the algorithms used to sign the message
  • c=relaxed/relaxed defines the canonicalization posture of the sending domain. Here, the configuration will make some reformating before hashing the message, such as putting the header names in lowercase, removing line trailing whitespace and such. Another option would be to use strict instead of relaxed, which would require the content to be 100% identical, or to see the validation fail. For example, c=relaxed/strict would allow the headers of the email to be reformated, but require the body to be strictly unchanged. The relaxed option is convenient to avoid unnecessary failures, as email servers can sometimes reformat the headers during processing
  • d=haveibeenpowned.com tells us which domain was the signature made for
  • h=[...] lists the headers that were present when the message was signed (and therefore were included in the hash)
  • s=s1 says that the selector for the domain’s public key is s1. It will be explained later
  • bh=[...] contains the base64 hash of the canonicalized body part of the message. Note that an l option could be provided in the parameters, to specify the max length of the body that will be used to calculate the hash. That means that content could be included after length l and the DKIM would still be valid
  • b=[...] contains the base64 signature

When the receiving server gets the message, it will try to see if the signature provided in b is valid. For that, it will make a DNS query to get the key for the domain that sent the email, and receive the following:

user@Host ~ % dig TXT s1._domainkey.haveibeenpwned.com
[...]
;; ANSWER SECTION:
s1._domainkey.haveibeenpwned.com. 300 IN CNAME	s1.domainkey.u3489673.wl174.sendgrid.net.
s1.domainkey.u3489673.wl174.sendgrid.net. 474 IN TXT "k=rsa; t=s; p=[key]"

You will notice the s1 in the dig query. This is the selector value that was in the s field of the DKIM header of the email. DKIM entries will always be stored for [selector]._domainkey.domain.tld.

Once it got the key provided in the DNS reply, the receiving server will verify that the signature is valid and matching the content. If not, the verification fails. Otherwise, something looking as follows will be added to the email headers.

Authentication-Results: mailin007.protonmail.ch; dkim=pass (1024-bit key)
 header.d=haveibeenpwned.com header.i=@haveibeenpwned.com header.b="EwFk1JDn"

Domain-based Message Authentication, Reporting and Conformance (DMARC)
#

If you followed everything until here, your email server address is now part of a SPF entry, and your messages are signed thanks to DKIM. This is good, but what happens if an attacker decides to forge a message, and send it from his server (without including any DKIM)? In this scenario, the email will likely be allowed by the recipient server, and end up in the recipient’s mailbox.

This is where DMARC comes in handy. This mechanism has the following benefits:

  • It allows giving guidelines to the receiving email servers on how to process emails failing SPF or DKIM checks (even if there is no obligation whatsoever for the servers to enforce it)
  • It allows extra options to manage SPF with subdomains (note that servers matching ~all will be marked as a fail)
  • It allows getting some feedback on the email sent using our domain name, which comes in handy for debugging or detecting malicious activity
  • DMARC checks that RFC5321’s Mailfrom header and the RDC5322’s Mailfrom header are matching to address a weakness in DMARC and SPF

As for the two other items, DMARC is set through a single TXT DNS field that will be located at _dmarc.domain.com. The following snippet shows an example of configuration.

v=DMARC1; p=reject; sp=reject; ruf=mailto:security@example.com; aspf=s; adkim=s; fo=1;

Let’s have a look at the different fields:

  • v (mandatory) is the DMARC version (always DMARC1)
  • p (mandatory) defines the policy for the domain sent from example.com in the case where the SPF or DKIM check fails. If it is set to reject nothing will end up in the users’ mailboxes, quarantine will send emails to spam, and none will do nothing
  • po does the same as p but for the sub-domains
  • ruf allows defining an email address that will receive forensic reports when emails fail the validation. rua is a similar option that will send daily (less detailed) aggregated reports of the activity involving our domain (e.g., if you send emails to Gmail during the day, Gmail will send you a report aggregating the various operations at the end of the day). Note that some servers will not send reports
  • aspf allows setting an extra policy for the SPF. It can either be strict (s) or relaxed (r). In the case where you have an SPF record for example.com, a mail sent from mail@test.example.com will fail the SPF validation if the policy is set to strict, else success if the policy is set to relaxed.
  • adkim does the same as aspf but for DKIM
  • fo allows setting the logging level when emails fail the validation. 0 (default) will send reports if both SPF and DKIM fail, 1 will send reports if any of DKIM or SPF fails, d will send an email if the DKIM fails, and s if this is the SPF. Note that it is possible to combine the rule, for example fo=0:d;.

Useful Tools
#

Here is a list of tools that I think are helpful when it comes to configuring DNS email entries.

  • mxtoolbox.com - Various online tools to allow checking your email configuration
  • mail-tester.com - will give a mark to the emails sent from your email server, and tell you if things are not working or should be improved
  • whatsmydns.net - allows you to check if DNS entries are properly propagated

References
#