What are DKIM and SPF and How do we use them?
Sender Policy Framework (SPF) and DomainKeys Identifier Mail (DKIM) are both methods of identifying email senders.
In an earlier post we discussed email reputation and why it's important. One of the critical factors in maintaining the reputation of your domain is making sure that you're the only one sending from it. In this article, we'll look at how SPF and DKIM can help to achieve this and improve your message deliverability.
Using SPF to identify the Sending IP(s) For a Domain
SPF is a mechanism for ensuring that a given mail server is permitted to send an email for your domain, given it's IP address.
Before SPF spammers would often forge email addresses and pretend to be sending email from another, legitimate, domain. If the domain (specified in the return-path) has an SPF record in place, then this can be used to determine if the server that's sending the email is permitted to send for this domain. This is especially important when you may have multiple web apps, marketing, support and corporate email systems sending messages on behalf of your domain.
Setting up and SPF record requires adding a TXT record to a domain's DNS in the correct format. In earlier versions of the specification, you could use a dedicated SPF record type, but this was later discontinued. For this reason, you may still see both types in action.
Since Paypal scams would be a pretty common use case for a Spammer sending Junk mail lets use their SPF record as an example and see how they can stop this. Lets say we sent an email with a return-path of support@paypal.com
(perhaps a contrived example but it will server the purpose). First, we'd lookup the DNS TXT record for paypal.com:
host -t TXT paypal.com
...
paypal.com descriptive text "v=spf1 include:pp._spf.paypal.com include:3ph1._spf.paypal.com include:3ph2._spf.paypal.com include:3ph3._spf.paypal.com include:3ph4._spf.paypal.com include:3ph5._spf.paypal.com ~all"
So what's happening here? A number of other TXT records have been omitted but we can see one record beginning with v=spf1
. This identifies that this record is an SPF entry and defines the version of SPF that's used on the domain.
Then we see the mechanisms and qualifiers used to determine if a given IP is permitted to send for this domain (paypal.com). These mechanisms will then be evaluated left to right to determine if the IP address is valid for the return-path of the email.
Mechanism | Description |
---|---|
INCLUDE |
Resolve and evaluate the policy of another domain, if that passes we will pass. However, if we fail, we'll continue to evaluate this record. |
A |
Does the IP match any of the IPs listed in an A record? |
IP4 |
Does the IP match any of the IPs in the given IPv4 range? |
IP6 |
Does the IP match any of the IPs in the given IPv6 range? |
MX |
Does the IP match any of the IPs listed in the given MX record? |
ALL |
Always matches. This is normally used to provide a default if other rules have no matched. Such as -all . |
We've excluded PTR
and EXISTS
because they're rarely used and PTR should be avoided.
Qualifier | Description |
---|---|
+ |
PASS , this can be omitted (e.g. +include is the same as include ). |
? |
NEUTRAL , the same as a none result. |
~ |
SOFTFAIL , a debugging aid, normally used to accept but highlight the fail. |
- |
FAIL , the message should be rejected. |
In our example above, we include pp._spf.paypal.com
. So we'll evaluate that.
host -t TXT pp._spf.paypal.com
pp._spf.paypal.com descriptive text "v=spf1 ip4:66.211.168.230/31 ip4:173.0.84.224/27 ip4:173.0.94.244/30 include:ppcorp._spf.paypal.com -all"
If our IP address is in the ranges listed or the included record then we'll PASS. If not this include will fail and we'll go back to the original record and continue the next include. Finally if none of the results pass we'll hit the -all
and FAIL.
When using CloudMailin inbound SPF records are validated against the sending IP automatically and the result is included in our HTTP POST. When using CloudMailin outbound we automatically set the return-path of emails so that we can validate the sending domain. We ask customers to add a CNAME record to ensure SPF alignment.
Correctly setting up an SPF record will help us get a step closer to determining that we're allowed to send for a given domain. However, we can also use DKIM.
Using DKIM to identify the Message Content for a Domain
DKIM is slightly more involved than SPF but provides a way to validate more than just the sending server. DKIM provides a way to use public/private key pairs to sign a message and authenticate that it considers the content of a message legitimate.
Just like HTTPS certificates on a website, DKIM makes use of public/private keys. Using a DNS record you post the public cryptographic key online for the receiver of the message to use. Then, when sending a message the sender uses the key to check that the signature provided on the message is valid.
We won't go into a huge amount of detail but take the following example of a DKIM signature in a message header:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.com;
s=f68fab271z; t=1589812302;
bh=B0eJT1fSuF8lK4aKbsNf/oIMT+J5Pst+9nUtHnn//N0=;
h=Date:From:To:Subject:From;
b=WBrXRyZ1Zig3Lns3yrzhWwL3vB6UwcKABwoCBHion/ZMZDv/rKtGYRTciz9XYVpkh
zSP/GJlPMNANtE0HKYCWLfvtrWNPaG5cRHx8WTpjyQg65uDeyTGTTD8xvSnfnBsNtZ
qbqopxmr1RJXthhsRtNEnvcJrtUM2bWOMkXXYgAM=
We'll take a look at the following parts of the signature:
Field | Description |
---|---|
v |
The version. |
a |
The signing algorithm used. |
d |
The domain we're authenticating. |
s |
The Selector. This is important because we may have multiple different systems signing emails for a given domain. The selector allows us to use a different key for each system. |
h |
The list of headers that have been signed. |
bh |
The hash of the body. |
Note that in our example the FROM header is listed twice. This is a process called over-signing. Adding the header twice ensures that if an attacker was to add a second FROM header then the signature would not match.
If we receive the signature above we can make a TXT query to the domain with the selector and get the following record back:
host -t TXT f68fab271z._domainkey.example.com
f68fab2704._domainkey.example.com descriptive text "v=DKIM1; h=sha256; k=rsa; s=email; " "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCXIn0ffyvb7EYORJgnbu77hv/ol02rK1KxmcPw3T5H6y1NerrdJuvPa7/zQaEUKjT0RPlcfiwvygRT1PUW8aopOL66gRZdo3Xz3cWGAuNy4iBnvssh3tlcX4R4z5tDtWtfAZXvaswkuaeSON49UN5tTCgSfOqIOeS/tjNIABm6NwIDAQAB"
If we also compute the hash of the given headers and body with the public key then we're able to validate that the message was signed by the indicated domain and hasn't been forged or modified in transit.
It's important to note that a DKIM only really validates that a message has been signed by a specific domain. It's up to the receiver to determine what happens on a pass or fail of this check.
CloudMailin outbound requires that users add a DNS txt record so that all outbound messages can be signed automatically. We'll generate the TXT record for you when you register a domain.
In Summary
As you can see, both SPF and DKIM have their advantages when it comes to preventing SPAM. They complement each other rather than overlap and it's recommended that both are used together to ensure that your email deliverability is at its peak.