DMARC can stop spoofed spam and phishing from reaching you and your customers, protecting your information security and your brand. However, complexity and misconceptions deter many organizations from ever deploying it. Part mythbusting , part implementation guide, this post explains the shortcomings of SPF and DKIM, what DMARC is, how to deploy DMARC properly, and how to respond to DMARC reports – all without the need for an additional vendor, thanks to open source software!
Modern email authentication relies on a combination of three standards: SPF, DKIM, and DMARC. These standards help ensure that a message came from a server related to the domain owner and was not spoofed.
Sender Policy Framework (SPF)
SPF was the first widely adopted standard for combating email spoofing. Despite its limitations in preventing spoofing, most email recipients expect you to have it deployed on your domain. For example, Gmail/G-Site/Google will throttle incoming emails from domains that do not have a valid SPF record.
How SPF works
SPF is defined in RFC 7208. It works by checking for a specially formatted DNS TXT record in the domain of the mail from header in the SMTP transaction. This SPF record describes which servers are authorized to send as that domain by using mechanisms to identify authorized IP addresses and hostnames, or even include the SPF records of other domains.
Every SPF record is a TXT record at the root of a domain or subdomain that starts with v=spf1. From there, mechanisms are used to describe mail servers are allowed (or not allowed) to send email as that domain or subdomain. A domain or subdomain can only have one SPF record, but each subdomain can have its own SPF record.
Some mechanisms like a, mx, include, and redirect use additional DNS lookups to work. SPF has a maximum DNS lookup limit of 10, including any included records. Any SPF record that would require more than 10 DNS lookups to resolve is invalid! This is a common mistake to make when deploying SPF.
To work around this limit, send email from different subdomains. Each subdomain needs its own SPF record and has its own set of limits for that record. For example, you could send newsletters from news.example.com, and invoices from billing.example.com
|ip4||Describes an ipv4 address or CIDR block of addresses.|
|ip6||Describes an ipv6 address or block of addresses.|
|mx||Describes the servers listed in the mx record of the domain. Counts towards the DNS lookup limit.|
|a||Describes the servers listed in the A and/or AAAA records of the domain. Counts towards the DNS lookup limit.|
|include||Includes the SPF record from the domain after the colon (it does not include the all modifier, if any). Counts towards the DNS lookup limit.|
|redirect||Stops processing the SPF record, and continues at the specified domain’s SPF record (including the all modifier!). Counts towards the DNS lookup limit.|
Mechanisms listed in the SPF record have an implicit pass (i.e. +) qualifier in front of them. Possible qualifiers are:
|+||pass||A “pass” result means the client is authorized to inject mail with the given identity. The domain can now, in the sense of reputation, be considered responsible for sending the message. Further policy checks can now proceed with confidence in the legitimate use of the identity.|
|?||neutral||A “neutral” result indicates that although a policy for the identity was discovered, there is no definite assertion (positive or negative) about the client.|
A “neutral” result MUST be treated exactly like the “none” result; the distinction exists only for informational purposes. Treating “neutral” more harshly than “none” would discourage domain managers from testing the use of SPF records.
With a “none” result, the SPF verifier has no information at all about the authorization or lack thereof of the client to use the checked identity or identities. The check_host() function completed without errors but was not able to reach any conclusion.
|~||softfail||A “softfail” result ought to be treated as somewhere between “fail” and “neutral”/”none”. The domain manager believes the host is not authorized but is not willing to make a strong policy statement.|
Receiving software SHOULD NOT reject the message based solely on this result, but MAY subject the message to closer scrutiny than normal.
|–||fail||A “fail” result is an explicit statement that the client is not authorized to use the domain in the given identity. Disposition of SPF fail messages is a matter of local policy.|
Most SPF records (except for those that are designed to be included in other SPF records) end with an all modifier. The all modifier consists of the word all with a qualifier in front of it. The all modifier states how emails should be treated that do not match any of the listed mechanisms.
SPF’s weakness: Relying on SMTP headers
It is a common misconception that SPF stops email spoofing. At best, it makes things a tiny bit more difficult on an attacker.
Remember, SPF checks for an SPF record at the domain in the mail from header in the SMTP transaction (also known as the envelope from), not the message from header that the receiving mail client sees, The SMTP transaction are not visible to the end client, even when viewing the message headers.
This means that an attacker can use SMTP headers to direct the target’s mail server to check a domain that the attacker controls, which contains an authorizing mechanism for the mail server the attacker is using, while spoofing a completely different domain for the target to see in the message from header!
In the example telnet screenshot below, the attacker is able to get the receiving email server to check the SPF record of a domain that the attacker controls (i.e..infosecspeakeasy.org), while spoofing the target’s own domain (i.e. cincykitchenandbath.com) in the message from header, which is the from address that the target user will see.
This sort of domain mismatch occurs legitimately when a mailbox rule forwards a message from another domain. The message from domain will stay the same, but the SMTP mail from header will contain the domain of the forwarding mail server.
Example SPF records
Once you know exactly which email services legitimately send as the domain (DMARC reports will tell you), update the SPF record accordingly, and change the ?all modifier to -all.
SPF record for domains that send emails from their incoming gateways and are missing SPF records
v=spf1 mx ?all
This record explicitly authorized any servers listed in the domain’s MX record, while treating all others as neutral. This is a good temporary SPF record for new domains or newly acquired domains that do not already have a SPF record.
Here are some good examples of SPF records for common cloud email providers:
v=spf1 include:spf.protection.outlook.com ?all
v=spf1 include:_spf.google.com ?all
v=spf1 a:dispatch-us.ppe-hosted.com a:dispatch-eu.ppe-hosted.com ?all
SPF record for domains that do not send email (e.g. parked domains)
This record explicitly states that no mail servers are authorized to send email as this domain.
This must be added to all domains that do not send email, inducing parked domains.
DomainKeys Identified Mail (DKIM)
DomainKeys Identified Mail (DKIM) is a email message authentication standard, defined in RFC 6376. Because DKIM authenticates the message headers , rather than the SMTP headers, DKIM authentication survives intact when a message is directly forwarded (e.g. via a mailbox rule).
DKIM message headers
Here’s an example DKIM header
DKIM-Signature: v=1; a=rsa-sha256; d=example.com; s=s1; c=relaxed/simple; l=1234; t=1117574938; x=1118006938; h=from:to:subject:date; bh=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=;b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZVoG4ZHRNiYzR
|a||Signature algorithm (rsa-sha256 should be used)|
|d||The domain where the public key can be found|
|s||The selector pointing to the public key at the domain (an arbitrary string)|
|h||A colon separated list of headers to concatenate when validating the header signature|
|b||The base64-enoded signature hash of the headers listed in the h tag|
|bh||The base64-enoded signature hash of the message body|
|t||Signature timestamp in UNIX timestamp format (i.e. the number of seconds from 00:00:00 on January 1, 1970 in the UTC time zone)|
|x||Signature expiration timestamp in UNIX timestamp format (i.e. the number of seconds from 00:00:00 on January 1, 1970 in the UTC time zone)|
|c||canonicalization algorithm: Defines if/how the receiving the receiving mail server should normalize the message to account for slight variations in whitespace and line breaks that could otherwise invalidate the signature|
relaxed mode is strongly recommended for the header and body canonicalization (i.e. c=relaxed/relaxed).
|i||Identity/user-agent of the signer|
|l||Number of characters from the begging of the body to use when calculating the body signature (not recommended)|
|z||Not well defined|
The receiving mail server uses the selector (s=) and domain (d=) tags to look up the public key as a DNS TXT record at
In the above signature example, the receiving server would look for the DKIM key at:
DKIM public key records are formatted as:
v=DKIM1; k=rsa; p=<public key>
DKIM key rotation
It is generally recommended to rotate DKIM keys once per month, or at least after you suspect that the DKIM private key has been compromised. Most email/marketing services will handle key rotation for you when you configure your domains for DKIM, but some almost never rotate their keys.
Unlike web and email certificates, the domain/email address is not part of the PKI. DKIM does not use certificates.. The domain is listed in the d= header tag.Therefore, you do not need a separate public/private key pair for each domain.
You should create two key pairs, and store the public keys under two different selectors, for example:
s1._domainkey.example.com TXT "v=DKIM1; k=rsa; p=<public key>" s2._domainkey.example.com TXT "v=DKIM1; k=rsa; p=<a different public key>"
Lines in DNS TXT records are truncated at 256 characters . The key must be split into separate lines in the same record in order to be valid.
With CNAME records, your other domains can use the same selectors and keys:
s1._domainkey.example.net CNAME s1._domainkey.example.com. s2._domainkey.example.net CNAME s2._domainkey.example.com.
Third party services will often have you authorize their DKIM keys on your domains using CNAME records, and will then validate that those records exist before signing email, so they can handle key changes for you.
If you are using your own keys, it is up to you to manage them, preferably using automation. Start by signing all of your emails using the first selector. When it is time to rotate they keys, sign all outgoing email using the s2 sector. After a week of not using the key at the s1 selector, replace the key at the s1 selector.
When it is time to rotate keys again, start using the s1 selector exclusively, wait a week, then replace they key at the s2 selector with a new key, so it will be ready for the next rotation.
Unless a key is known to have been compromised, it is important to wait a week (i.e. 7 days) before replacing it, as some receiving mail servers will cache the public key at a given selector for up to a week.
Using this ping-pong key rotation scheme, you ensure that email is always signed with a valid, secure key.
- Start exclusively signing with the other selector
- Wait 7 days
- Replace the key at the old selector so it is ready for the next rotation
- Go to step 1
DKIM’s weakness: arbitrary d= values
Without DMARC, the d= value in a DKIM signature header is not required to match the same domain that the user sees in the message From header. An attacker can place a valid DKIM signature header in an email with a d= value that points to a domain the attacker controls, allowing DKIM to pass while still spoofing the from address to the user.
Domain-based Message Authentication, Reporting, and Conformance (DMARC)
Domain-based Message Authentication, Reporting, and Conformance (DMARC) is defined in RFC 7489. DMARC ensures that SPF and DKM authentication mechanisms actually authenticate against the same base domain that the end user sees.
In order to be useful, a DMARC DNS record must be published by a domain owner, and email recipients must honor this record, including its enforcement policy, and at least send back aggregate reports if requested by the domain owner.
A message passes a DMARC check by passing DKIM or SPF, as long as the related indicators are also in alignment.
|Passing||The signature in the DKIM header is validated using a public key that is published as a DNS record of the domain name specified in the signature||The mail server’s IP address is listed in the SPF record of the domain in the SMTP envelope’s mail from header|
|Alignment||The signing domain aligns with the base domain in the message’s from header||The domain in the SMTP envelope’s mail from header aligns with the base domain in the message’s from header|
DKIM alignment is more important than SPF, because only DKIM remains aligned when a message is forwarded via a mailbox rule.
DMARC has two report types: aggregate and forensic. email recipients that honor DMARC send these reports back to domain owners in emails sent to addresses listed in the domain’s DMARC record. There reports contain very useful information for debugging message alignment and identifying malicious spoofing campaigns.
|Compressed XML files sent at least once per day by recipient domains to the URIs listed in the rua DMARC tag|
Records number of messages sent from a IP address, and the SPF and DKIM status
These reports are sent regardless of a success or failure, so that domain owners have a view of all mail authentication of messages appearing to be from their domain
|An email with an email that failed the DMARC check attached (RFC 822/.eml format) sent to the URIs listed in the ruf DMARC tag|
These can be very useful for DMARC troubleshooting and phishing investigations. However, most email recipients do not send forensic reports, or may only supply the message headers for privacy reasons
DMARC requires domain owners to set a policy (p=) tag in their DMARC record. This policy tells recipients how they should react to an email that appears to come from that domain based on the message from header, but does not pass DMARC alignment.
|none||The Domain Owner requests no specific action be taken regarding delivery of messages. Use this first to ensure your messages are DMARC compliant before switching to quarantine or reject.|
|quarantine||The Domain Owner wishes to have email that fails the DMARC mechanism check be treated by Mail Receivers as suspicious. Depending on the capabilities of the Mail Receiver, this can mean “place into spam folder”, “scrutinize with additional intensity”, and/or “flag as suspicious”.|
|reject||The Domain Owner wishes for Mail Receivers to reject email that fails the DMARC mechanism check. Rejection SHOULD occur during the SMTP transaction.|
DMARC policy records are placed at a TXT record at the _dmarc subdomain. Subdomains of the TLD/base domain automatically inherate this DMARC policy record, or they can have their own record at their own _dmarc subdomain.
Here is an example DMARC policy record
_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:firstname.lastname@example.org; ruf=mailto:email@example.com"
These tags tell recipients where to send reports
|rua||A comma separated list of mailto: address to send aggregate reports to|
|ruf||A comma separated list of mailto: address to send forensic reports to|
You might not want to include this tag if you are in an industry that sends sensitive information, especially if you are automatically processing reports. This could cause sensitive emails to be saved in ELK or other storage areas if the messages fail DMARC.
If a recipient receives an sensitive email that fails DMARC, that email could be sent back to your reporting inbox, and archived in your DMARC report processing tool.
Recipients are only obligated to send reports to the first two rua and ruf addresses. They might not send to any additional address listed.
Not recommended tags
|sp||Default mirrors p tag’s value|
Sets the policy for all subdomains. Setting this tag could allow the spoofing of any arbitrary subdomain Add a separate policy record for each subdomain as needed instead.
|pct||Default is 100|
Sets the percentage of mail to apply the DMARC policy to. Set p=none when testing instead to ensure all mail is treated equally
|adkim||Default is relaxed (r).|
In relaxed mode, the Organizational Domains of both the DKIM-authenticated signing domain (taken from the value of the “d=” tag in the signature) and that of the RFC 5322 From domain must be equal if the identifiers are to be considered aligned.
|aspf||Default is relaxed (r)|
In relaxed mode, the SPF-authenticated domain and RFC5322 From domain must have the same Organizational Domain. In strict mode, only an exact DNS domain match is considered to produce Identifier Alignment.
|fo||Default is 0|
Provides requested options for generation of failure reports. Report generators MAY choose to adhere to the requested options. This tag’s content MUST be ignored if a “ruf” tag is not also specified. The value of this tag is a colon-separated list of characters that indicate failure reporting options.
|rf||A list separated by colons of one or more report formats as requested by the Domain Owner to be used when a message fails both SPF and DKIM tests to report details of the individual failure. Only “afrf” (the auth-failure report type) is currently supported in the DMARC standard.|
|ri||Default is 86400|
Indicates a request to Receivers to generate aggregate reports separated by no more than the requested number of seconds. DMARC implementations MUST be able to provide daily reports and SHOULD be able to provide hourly reports when requested. However, anything other than a daily report is understood to be accommodated on a best-effort basis.
If an email address in rua or ruf has a different base domain than the domain of the policy record, an authorization record must be added to the base domain of the email address to indicate that it accepts reports about that domain. For example, if firstname.lastname@example.org also needed to accept reports for example.net, the poly record for example.net would look like this:
_dmarc.example.net TXT "v=DMARC1; p=none; rua=mailto:email@example.com; ruf=mailto:firstname.lastname@example.org"
Because example.net is a different base domain than example.com, the following record needs to be addded to example.com to indicate that it accepts reports about example.com:
example.net._report._dmarc_example.com TXT "v=DMARC1"
How can I check a DKIM signature?
Send an email to a Gmail account. They have a nice UI that shows the DKIM status of a message.
What if a third party sender can’t support DMARC?
- Some vendors don’t know about DMARC yet; ask about SPF and DKIM/email authentication.
Check if they can send through your email relays instead of theirs.
- Do they really need to spoof your domain? Why not use the display name instead?
- Worst case, have that vendor send email as a specific subdomain of your domain (e.g. email@example.com), and then create separate SPF and DMARC records on news.example.com, and set p=none in that DMARC record.
Do not alter the p or sp values of the DMARC record on the Top-Level Domain (TLD) – that would leave you vulnerable to spoofing of your TLD and/or any subdomain.
Further reading on this problem.
- Configure email gateways to honor DMARC records and send aggregate DMARC reports
- Inventory domains
- Deploy SPF
- Deploy DKIM
- Set up mailbox for receiving DMARC reports
- Deploy DMARC DNS records
- Monitor incoming DMARC reports
- Adjust SPF, DKIM signing, and DMARC policies as needed
DMARC deployment guides
- DMARC Overview
- DMARC video how to for Proofpoint
- DMARC guide for G Suite
- DMARC guide for Office 365
- DMARC guide for Cisco Email Security Appliances (ESAs)
- DMARC guide for Cisco Cloud Email Security (ESAs)
- Generic DMARC Deployment guide – Dmarcian
- List of DMARC Support Status of SaaS Services – Dmarcian
- DMARC Guide for 3rd Party Senders – Dmarcian
- Solutions to common problems – Dmarcian
- Reference library by OnDMARC
- SPF Deployment Guide – MSDN
- RFC 7489
SPF and DMARC record validators
- checkdmarc – A Python module and CLI tool I wrote to validate and parse SPF and DMARC records
- trustymail – By DHS; checks for compliance with BOD 18-01, including SPF, DMARC, and STARTTLS
DMARC report processing services
- Agari – Most popular provider to federal agencies, partners with NH-ISAC and others – “Contact us” pricing
- Dmarcian – Public, straightforward pricing, free public reference guides
- OnDMARC – Low cost services, extensive free public reference guides
- Proofpoint Email Fraud Defense – “Contact us” pricing; most useful for current Proofpoint customers
- Valimail – Offers automated SPF/DKIM/DMARC DNS record generation
- parsedmarc – Open source self-hosted DMARC report processing and analytics
- Email Authentication Policy and Deployment Strategy for Financial Services Firms (BITS/The Financial Services Roundtable – Feb. 2013)
- DHS Binding Operational Directive (BOD) 18-01 (Oct. 2017)
- Fifty-Seven Percent of Email “From” Healthcare Industry is Fraudulent (NH-ISAC – Nov. 2017)
DMARC compliant email relay services
Reasonably priced, fully DMARC compliant marketing and transactional email.
Good option if a full CRM is needed.
Extremely cheap bulk marketing email; extremely expensive transactional email.
A MailChimp add-on service for transitional email
- Set SMTP envelope sender for SPF alignment in the Mandrill dashboard
Settings -> Domains -> Tracking and Return Path Domain
Email and SMS marketing.
Salesforce Marketing Cloud
Not as cheap as Elastic Email, but cheaper than SendGrid and Mandrill, with options for SMS.
Services that must use your SMTP relays to be fully DMARC compliant
Known incompatible services
These serveries use their own domain came in the DKIM d= tag, making the signature unaligned with the message from address domain they are spoofing.
To work around this problem, configure the service to use a specific subdomain of your domain in the message from address for example, configure a newsletter to be sent from firstname.lastname@example.org, instead of email@example.com. That way, you can create a separate DMARC record with p=none for the news.example.com subdomain, while still being able the DMARC record for example.com TLD (and all other subdomains) at an enforced policy, like p=reject, when you are ready.
_dmarc.example.com TXT "v=DMARC1; p=reject; rua=mailto:firstname.lastname@example.org; ruf=mailto:email@example.com" _dmarc.news.example.com TXT "v=DMARC1; p=none; rua=mailto:firstname.lastname@example.org; ruf=mailto:email@example.com"