A Beginner's Guide to Technical Email Security Controls: Understanding SPF, DKIM, and DMARC
Proud Slytherin. Reformed red teamer. Infosec professional?
It wouldn’t be technology without even more acronyms! In today’s version of Buzzword Soup, let’s take a look at SPF, DKIM, and DMARC, and how they may help stop the phishing scourge plaguing our inbox!
In 2018, 93% of breaches began through a phishing or pretexting campaign (Verizon DBIR 2018
). All it takes is one user falling for a campaign to potentially put your company at risk. This is why it’s extremely important to make sure the fundamental technical controls are in place, so that you can work on educating your users knowing that the appropriate controls are in place.
Three key controls have been created to help combat phishing and spam — while there may be similarities in a few of these, there are enough differences it is important to implement all three — SPF, DMARC, and DKIM.
Sender Policy Framework (SPF) is simply a DNS TXT record that lists the servers that are allowed to send mail for a specified domain. This allows recipients to determine if an email really is coming from who it says it’s coming from. The recipient can then take an action based on whether the sender was authorized. Typically, recommended actions include a “soft fail” or a “hard fail” — a hard fail recommends not delivering the message at all, and a soft fail recommends delivering, but flagging it as suspicious.
Problems with SPF
- SPF records are really just a suggestion. There is nothing that requires a receiving server to take any action based on the record.
- SPF records only check the Return-Path header, leaving all other “from”-type headers available for spoofing, such as Envelope-From, Reply-To, etc.
- SPF checks can time out depending on how many DNS lookups are made. To prevent SPF from being used to perform a Denial of Service (DoS) attack, SPF implementations allow a maximum of ten DNS lookups that are required to fully resolve an SPF record — lookups can be caused through use of the “include”, “a”, “mx”, “ptr”, and “exists” mechanisms as well as the “redirect” modifier.
DKIM, or DomainKeys Identified Mail, is a complement to SPF, and establishes a mechanism to digitally sign outgoing email from your domain. This protects against emails that could have been altered in transit between the sender and the recipient by establishing a protocol that verifies that the organization delivering the email has the right to deliver the message.
There is no requirement for users to change their behavior, unlike using something such as PGP or S/MIME, and DKIM has been adopted by most major email providers.
So what does DKIM actually do?
Recipients of an email are able to leverage DKIM to confirm the sender’s identity and determine if the message was altered at any point during transit. To do this, the sender’s mail transfer agent (MTA) will hash a subset of message headers, and signs this hash using a private key. The public key will be stored in a DNS TXT record. The receiving MTA can then look up the public key for the sending domain and validate the signature and obtain the original hash values, which are then compared to the newly created hash values. If the values match, then DKIM was valid.
When a domain is configured for DKIM, email messages from their domain will be sent with a DKIM-Signature header, containing:
- How the message signing was implemented
- How long the signature is valid for
- What domain the signature is valid for
- Hash of the message’s headers and body, which can be used to validate the message wasn’t tampered with
To configure DKIM, check the settings in your email provider. The email provider will generate a public/private keypair, and provide the public key needed for your DNS TXT record.
Important Note: DKIM by itself is not a reliable method of authenticating the identity of the email sender, and does not prevent spoofing the visible domain in the “From” header.
So with all the issues present in SPF, namely that it relies on the receiver to take action, along comes our new savior…Domain-Based Message Authentication, Reporting, & Conformance! Yup, DMARC is much easier to say. In short, DMARC allows you the sender to indicate that the message is protected with SPF and/or DKIM, and to specify a policy with clear instructions for the receiver to follow if an email doesn’t pass SPF or DKIM (e.g. reject message, mark as spam, etc.). DMARC will also provide a report on messages that both pass and fail DMARC evaluation.
While DMARC uses SPF as one of it’s foundations, a host of other features have been added:
- Focuses on the “From” header that a user can see in their email client, and requires the SPF domain to match this domain
- SoftFail and Fail from SPF are both simply treated as Fail
- Reporting functionality that reports to the sender of the “From” domain, allowing them to see if their domain is being abused or to troubleshoot delivery
- Policy on what to do with a message is enforced by the receives, whereas SPF does not require enforcement
Some technical info
When configuring your SPF record, you can use one of four modifiers per mechanism:
- + Pass
- - Fail
- ~ Soft Fail
- ? Neutral (same as none, not really worth using in my opinion. This neither passes nor fails)
There are 7 possible results of an SPF evaluation:
- Pass: The sender is authorized and the message should be accepted
- Fail: The sender is not authorized and the message should be rejected
- SoftFail: The sender is not authorized, and the message should be tagged but accepted with a higher spam score
- Neutral: The sender is neither authorized nor unauthorized, and the message should be subject to other available spam detection controls
- TempError: An error occurred reading the SPF record, and the receiver must make a decision on what to do
- PermError: The SPF record is improperly configured, and the receiver must make a decision on what to do
- None: No SPF found
So what does a DKIM record look like? The record relies on the use of a DKIM Selector, which is used to point to a specific DKIM record in your DNS (this will be a DNS TXT record). Your public key record will then look like selector._domainkey.domain. For example, on my domain, the DKIM record looks like protonmail._domainkey.caliburnsecurity.com. The value of the record looks like:
So let’s break down what these different fields are:
v=DKIM1 — This is the version of the DKIM record. There is only one version in use currently, and this is the first tag. Any deviation from this, and the record will be ignored entirely (while it is optional to include this tag, it is highly recommended to use it).
p=… — This is the public key used by the email provider to match the DKIM signature generated using the private key. This is generated along with the private key during the DKIM set-up process.
k=rsa — This optional tag specifies that the key type is rsa. Note that RSA is the default value, and must be supported by both the signers and the verifiers
There are a few other tags that may be useful:
n — This tag allows you to provide notes that may be useful to an individual looking at the header. This tag should be used sparingly, given the character limits of a DNS record. This should be used by administrators.
g — Granularity of the key, with a default of “*”. This value must match the local-part of the “i=” tag. The purpose of this flag is simply to constrain which signing address is allowed to use the selector record.
s — The service type to which the record applies. A default value of * is applied, which allows all service types. The only other accepted value at this time is “email”. This tag is more forward looking, and as such can be skipped over for now.
t — This provides a list of flags that can be used to modify the interpretation of the selector.
- y -The domain is testing DKIM
- s — Specifies that the any DKIM signature header using the “i=” tag must have the same domain value on the right-hand side of the @ sign in both the “i=” tag and the “d=” tag of the signature. This tag should not be included if a subdomain is required.
Great, so we talked a bit about the public key used for DKIM, stored in a DNS TXT record. Now how do we actually go about using this?
Here is an example of a Dkim-Signature header on a marketing email that I received recently from Atlassian:
Dkim-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
The common fields used in a signature:
v=1 — The version used for DKIM, currently only at version 1. This must be included.
a — The algorithm used to generate the signature (required). Verifiers must support both “rsa-sha1” and “rsa-sha256”. Signers should sign using “rsa-sha256” unless the system does not have enough computational resources.
c — This optional tag informs the verifier of the type of canonicalization used to prepare the message for signing. There are two names, used to indicate the algorithms used for both the header and the body. If only one name is used, that name is used for the header, and “simple” is used for the body. Options include “relaxed” or “simple”
- d — The domain sending the email
- t — The timestamp of when the message was signed
- bh — The hash of the canonicalized message body content
- h — Headers which the dkim signature will cover, separated by colons
- s — The name of the selector used in DNS
- b — The signature of the message headers, including DKIM-Signature
So now that we think DMARC sounds awesome, how do we go about implementing it? Well, you guessed it, we are looking at another DNS TXT record!
Let’s take a look at my DMARC record for caliburnsecurity.com:
Let’s explore the tags available in these records (note — Only “v” and “p” are required tags; all others are optional):
- v=DMARC1 — The version of DMARC. Currently only DMARC1 is a valid version. If anything else is specified, or the “v” tag does not appear first, then the record will be ignored.
- p=none — The request mail receiver policy. This is the policy for the organizational domain. There are three possible values: “none”, “quarantine”, and “reject”. When selecting “none”, the domain owner is requesting no specific action to be taken by the receiver. “Quarantine” requests that mail failing authentication be treated as suspicious, but that the receiver is at liberty to do what they wish with suspicious messages (e.g. place in spam/junk folder, flag as suspicious, etc.). Last, if setting “reject”, the domain owner is requesting that the receiver rejects the message during the SMTP transaction. This is the highest level of protection.
- firstname.lastname@example.org — This is the reporting URI for aggregate reports.
Some other fields that aren’t included (which I may start adding soon):
- sp — Policy for subdomains of the organizational domain. A value of “none” states that the policy applies to only the specified domain, and not any subdomains. This will require each subdomain to have their own DMARC policy.
- adkim — Alignment mode for DKIM, with a value of either “strict” (marked as adkim=s) or “relaxed” (the default, adkim=r). The difference between the two is whether the DKIM domain and Header From domain are an exact match, or also a parent/child match (e.g. caliburnsecurity.com and child.caliburnsecurity.com). Under strict mode if the DKIM domain is mail.protonmail.com and the Header From domain is protonmail.com, the result will be a fail.
- aspf — Alignment mode for SPF, with similar values to adkim. In this case, for strict mode the domain name in the MAIL FROM command (SMTP) and the Header From domain must match exactly, and in relaxed mode any subdomain is acceptable in the MAIL FROM command.
- pct — Percentage of messages subject to filtering (0–100, with 100 as the default). Lower amounts should be used when testing before moving to 100.
- ruf — Reporting URI for forensic reports. This was originally intended to provide domain owners with a redacted copy of the email that failed DMARC, and to provide threat intelligence activities to extract malicious URLs. However, due to privacy and liability concerns over not redacting all relevant information, this type of reporting is not often provided by DMARC report generators.
- rf — Format for message failure reports. Default is Authentication Failure Reporting Format — “AFRF”. The only other acceptable value is “iodef” — Incident Object Description Exchange Format.
- ri — The time in seconds between reports requested from the receiving MTA. Default is set to 86400 (24 hour). Any number under 3600 will be rounded up to 3600, and hourly reports are sent on a best effort basiss.
- fo — Error reporting policy requested from the receiving MTA. Multiple values can be colon-separated. Valid values include “0” (Generate report if all underlying checks failed), “1” (Generate report if any underlying check failed), “d” (generate a report of DKIM failed), “s” (generate a report if SPF failed)
This article is meant to be a very basic introduction to three technologies. Please see below references for more information:
Subscribe to get your daily round-up of top tech stories!