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.
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
.
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
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.
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