Preventing Email Spoofing

with SPF, DKIM and DMARC

Posted by David on April 11, 2019
Read time: 7 mins

Preventing email spoofing is important to all domain owners, even if you are not using your domain for email services as it affects the reputation of your domain. In this post I will talk about how to prevent email spoofing with SPF, DKIM and DMARC.

Sender Policy Framework (SPF)

SPF allows the receiver to check that an email claiming to come from a specific domain comes from an IP address authorized by that domain’s administrators. The list of authorized sending hosts and IP addresses for a domain is in the SPF record for the domain which is published in the DNS TXT records for that domain.

How it works

The receiver checks if the sender is valid by querying the domain in the message header field Return-Path aka MAIL FROM for SPF record and checking the sender’s IP against that record.

Setup

Place the SPF record at the root of your domain.

Directive Effect
-all non-matching emails will be rejected
~all non-matching emails will be accepted but marked
+all allows any ip to send email from your domain.

This SPF record below will allow emails from 10.0.0.1, 2001:0db8:85a3:0000:0000:8a2e:0370:7334 and all the allowed IPs from the spf record from example.com and reject emails sent from all other IPs.

1
v=spf1 ip4:10.0.0.1 ip6:2001:0db8:85a3:0000:0000:8a2e:0370:7334 include:example.com -all

For more options check out the rfc.

Things to Note

SPF records cannot include more than ten dns queries.

A single TXT record cannot be more than 255 characters, but we can concate TXT records to form a SPF record of a longer length.

The DNS overhead for a reply that contains a single TXT record with two strings is about 34 bytes, plus the length of the hostname that’s being queries (e.g. “spf.example.com” is 15 bytes). So to keep within the 512 byte limit you need to break your SPF into chunks of no more than 478 minus the length of the hostname.

Another option is SPF record chaining.

You can check your spf record using this SPF Checker.

Macros

It is possible to create more complex SPF records using marcros.

Problem

SPF does not validate the From header which is shown in most clients as the actual sender of the message, but uses the Return-Path to determine the sending domain.

For example, a email was sent with the Return-Path of example.com and a From field of [email protected] assuming dzhy.dev have SPF setup to not allow any IPs, if example.com has a SPF record that allows my IP, the SPF check will still pass, because it does not check the From field, instead it checks the Return-Path.

Domain Keys Identified Mail (DKIM)

DKIM checks that an email was indeed send and authorized by the owner of that domain using digital signatures. This DKIM signature is a header that is added to the message and is secured with encryption.

How it works

The DKIM signature is generated by the MTA (Mail Transfer Agent). It creates a hash of the email. This hash value is encrypted using the private key and attached to the email as the DKIM signature in the header.

After receiving the email, the receiver verifies the DKIM signature using the public key in the TXT record of the domain specified in the signature, by decrypting the DKIM signature and comparing the decrypted hash value with the hash value of the email it received. If these two hashes are the same the MTA knows that the email has not been altered. This gives the user confirmation that the email was actually sent from the listed domain.

Setup

Refer to your email service provider’s documentation. Typically all you have to do is to add a DNS TXT record to your domain.

Finding DKIM selector

The DKIM record is at selector._domainkey.dzhy.dev, the selector can be any string, we can only find out the selector of a domain if we have the DKIM-Signature, of a email sent by that domain.

In the signature, the s field is the selector, so in this case s=zoho means the selector is zoho and the DKIM public key will be at zoho._domainkey.dzhy.dev.

1
2
3
4
5
6
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1554622568; 
s=zoho; d=dzhy.dev; [email protected]; h=Date:From:To:Message-Id:Subject:MIME-
Version:Content-Type; l=721; bh=YLjE/ckf0hWWS6SSYjvfMS06DZjBcD0C0ignKAs7TUs=;
b=HT4wf4u2t5PiApv9zgAWiyTbC8dwoE5qV7vZbGO+/1t+XUPOOlwGV3tfM/0/GqYl
oVtsVDqykG1B2iKGlXIDJGSU6qzsuzxCmHlv3YaddIR7WWLteCvMM4lOg3ZzwSHtiNx 
dVwG2fJ/iSgzB1AFrSQJa/C21ZewOKL0J8piwKYg=

Problem

The dkim signature can be valid and have no relation to the From header.

For example, I can have the following DKIM signature

1
2
3
4
5
6
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1554622568; 
s=zoho; d=dzhy.dev; [email protected]; h=Date:From:To:Message-Id:Subject:MIME-
Version:Content-Type; l=721; bh=YLjE/ckf0hWWS6SSYjvfMS06DZjBcD0C0ignKAs7TUs=;
b=HT4wf4u2t5PiApv9zgAWiyTbC8dwoE5qV7vZbGO+/1t+XUPOOlwGV3tfM/0/GqYl
oVtsVDqykG1B2iKGlXIDJGSU6qzsuzxCmHlv3YaddIR7WWLteCvMM4lOg3ZzwSHtiNx 
dVwG2fJ/iSgzB1AFrSQJa/C21ZewOKL0J8piwKYg=

which is for the domain dzhy.dev but my From field is [email protected], this will be valid because the signature is valid for the given domain dzhy.dev, it does not check the signature using the domain in the From field.

Domain-based Message Authentication, Reporting & Conformance (DMARC)

DMARC builds on the widely deployed SPF and DKIM protocols, to improve and monitor protection of the domain from fraudulent email. It is meant to fix the issues of SPF and DKIM. Implementing SPF and DKIM by themselves does not prevent spoofing, but combined with DMARC they will work great.

DMARC requires authentication alignment with the domain used in the header From, for both SPF and DKIM. Meaning the domain in DKIM must share the same organizational domain as the From header or be the exact same depending on the configuration, the domain for SPF the Return-Path and the header From should share an organizational domain or be the exact same depending on the configuration.

How it works

The receiver extracts the domain in the From header, and queries for a DMARC policy record of that domain. If it exists, perform DKIM and SPF checks, then perform Identifier Alignment checks. Emails that fail the DMARC mechanism check are disposed of in accordance with the discovered DMARC policy of the Domain Owner.

Setup

Domain Owner DMARC preferences are stored as DNS TXT records in subdomains named _dmarc. For example, the Domain Owner of example.com would post DMARC preferences in a TXT record at _dmarc.example.com.

The table below explains each tag of the sample DMARC policy.

Tag Effect
v=DMARC1 Specify DMARC version
p=reject Reject any mail that fails the DMARC check
rua=mailto:[email protected] Send aggregate reports to [email protected]
adkim=s DKIM Identifier Alignment strict mode
adkim=s SPF Identifier Alignment strict mode
ruf=mailto:[email protected] Send failure reports to [email protected]
fo=1 Generate a DMARC failure report if any checks failed

By default daily aggregate reports will be sent if you set a rua address.

Sample DMARC policy.

1
v=DMARC1; p=reject; rua=mailto:[email protected]; adkim=s ; aspf=s ; ruf=mailto:[email protected] ; fo=1

For other options refer to rfc7489 Section 6.3

Resources