Demystifying DMARC: A guide to preventing email spoofing

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!

Direct link to presentation

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

MechanismDescription
ip4Describes an ipv4 address or CIDR block of addresses.
ip6Describes an ipv6 address or block of addresses.
mxDescribes the servers listed in the mx record of the domain. Counts towards the DNS lookup limit.

mxThe servers listed in the mx records of its own domain
mx:example.comThe servers listed in the mx records of example.com
aDescribes the servers listed in the A and/or AAAA records of the domain. Counts towards the DNS lookup limit.

aThe IP addresses listed in the domains’ own A/AAAA records
a:example.comThe IP addresses listed in the A/AAA records of example.com
includeIncludes the SPF record from the domain after the colon (it does not include the all modifier, if any). Counts towards the DNS lookup limit.
redirectStops 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:

ModifierNameDescription
+passA “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.
?neutralA “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.

~softfailA “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.

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

A screenshot showing how SPF can be bypassed by spoofing the SMTP mail from header
A screenshot showing how SPF can be bypassed by spoofing the SMTP mail from header

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:

Office 365

v=spf1 include:spf.protection.outlook.com ?all

G-Suite

v=spf1 include:_spf.google.com ?all

Proofpoint Essentials

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)

v=spf1 -all

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

Required tags

TagDescription
vSignature version
aSignature algorithm (rsa-sha256 should be used)
dThe domain where the public key can be found
sThe selector pointing to the public key at the domain (an arbitrary string)
hA colon separated list of headers to concatenate when validating the header signature
bThe base64-enoded signature hash of the headers listed in the h tag
bhThe base64-enoded signature hash of the message body

Optional tags

TagDescription
tSignature timestamp in UNIX timestamp format (i.e. the number of seconds from 00:00:00 on January 1, 1970 in the UTC time zone)
xSignature 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)
ccanonicalization 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).

iIdentity/user-agent of the signer
lNumber of characters from the begging of the body to use when calculating the body signature (not recommended)
zNot 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

selector(s=)._domainkey.domain(d=)

In the above signature example, the receiving server would look for the DKIM key at:

TXT s1._domainkey.example.com

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.

  1. Start exclusively signing with the other selector
  2. Wait 7 days
  3. Replace the key at the old selector so it is ready for the next rotation
  4. 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.

DKIMSPF
PassingThe 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 signatureThe mail server’s IP address is listed in the SPF record of the domain in the SMTP envelope’s mail from header
AlignmentThe signing domain aligns with the base domain in the message’s from headerThe 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 reports

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.

Report typeDescription
Aggregate
(rua)
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

Failure/Forensic
(ruf)
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 policies

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.

TagDescription
noneThe 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.
quarantineThe 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”.
rejectThe Domain Owner wishes for Mail Receivers to reject email that fails the DMARC mechanism check. Rejection SHOULD occur during the SMTP transaction.

Policy records

DMARC policy records are placed at a TXT record at the _dmarc subdomain. Subdomains of the TLD/base domain automatically inherit 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:dmarc@example.com; ruf=mailto:dmarc@example.com"

Required tags

TagDescription
vDMARC version

DMARC1

pDMARC policy

Recommended tags

These tags tell recipients where to send reports

TagDescription
ruaA comma separated list of mailto: address to send aggregate reports to
rufA 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

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

pctDefault 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

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

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

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

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

Authorization records

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 dmarc@example.com 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:dmarc@example.com; ruf=mailto:dmarc@example.com"

Because example.net is a different base domain than example.com, the following record needs to be added to example.com to indicate that it accepts reports about example.com:

example.net._report._dmarc_example.com TXT "v=DMARC1"

FAQs

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.

A screenshot showing how DKIM signature alignment can be verified using Gmail's UI
A screenshot showing how DKIM signature alignment can be verified using Gmail’s UI

What if a third party sender can’t support DMARC?

  1. 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.
  2. Do they really need to spoof your domain? Why not use the display name instead?
  3. Worst case, have that vendor send email as a specific subdomain of your domain (e.g. noreply@news.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.

Deployment steps

  1. Configure email gateways to honor DMARC records and send aggregate DMARC reports
  2. Inventory domains
  3. Deploy SPF
  4. Deploy DKIM
  5. Set up mailbox for receiving DMARC reports
  6. Deploy DMARC DNS records
  7. Monitor incoming DMARC reports
  8. Adjust SPF, DKIM signing, and DMARC policies as needed

DMARC deployment guides

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

DMARC adoption

DMARC compliant email relay services

Constant Contact

Elastic Email

Reasonably priced, fully DMARC compliant marketing and transactional email.

HubSpot

Good option if a full CRM is needed.

MailChimp

Extremely cheap bulk marketing email; extremely expensive transactional email.

Mandrill

A MailChimp add-on service for transitional email

Mailgun

Mailjet

Email and SMS marketing.

Net-Results

Marketing automation

Salesforce Marketing Cloud

SendGrid

Sendinblue

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 noreply@news.example.com, instead of noreply@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:dmarc@example.com; ruf=mailto:dmarc@example.com"
_dmarc.news.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com; ruf=mailto:dmarc@example.com"

2 thoughts on “Demystifying DMARC: A guide to preventing email spoofing

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.