In 2010, engineers from 17 organizations (including Google, Paypal, LinkedIn, and Microsoft) met to discuss combating spam, especially the increasing spoofing/counterfeit varieties, at scale. After several drafts, DMARC was released in 2015 as RFC 7489.
Uptake was slow, like it usually is with new standards. However, by 2021, there were nearly 5 million valid DMARC policies.
What is DMARC?
DMARC (technically Domain-based Message Authentication, Reporting, and Conformance, but that's a mouthful) extends the existing SPF and DKIM standards. At its core, DMARC is two things in one:
- It's an email authentication protocol that verifies emails are actually coming from the domain they claim to be coming from
- It's a reporting system that tells you when your emails fail DMARC authentication (in other words: they might be spoofed)
Why do you need DMARC?
In 2013, Facebook and Googles employees received an invoice from Quanta Computer, a Taiwan-based computer hardware manufacturer. The invoices seemed normal — they'd done business with Quanta Computer before — so they transferred the amount due and moved on.
Except the invoices didn't exist. These emails weren't from Quanta Computer at all, but a spoofer named Evaldas Rimasauskas. His fake emails looked like they came from legitimate Quanta employees. Facebook and Google lost over $100 million before the scam was found.
In this attack, the only affected parties were Facebook and Google. That's not always true.
The 2017 Amazon Locky attack targeted Amazon customers by spoofing real shipping updates sent from email@example.com. The only content in the email was a Microsoft Word file. Anybody who downloaded it would find their device locked with a $250-$500 ransom.
DMARC's primary purpose is to stop people from spoofing your email address. However, by raising the level of trust in emails from your domain, it also indirectly helps your email deliverability. Properly configured SPF/DKIM and DMARC records are signals of good sender behavior, and can improve your sender reputation.
How does DMARC work?
Unlike SPF and DKIM, which are both self-contained, DMARC has to piggyback off either SPF or DKIM for it to function.
A DMARC test will pass if either SPF or DKIM passes and that pass is aligned.
DMARC enforces alignment between either the SMTP
MAIL FROM domain (SPF domain) and the
FROM header domain, or the domain on a DKIM signature and the
FROM header domain. In both cases, think of the
FROM header domain as the anchor.
If the email passes the receiving server's DMARC test, it's a big tick for that email's trust profile and it'll likely be treated favourably.
If the email fails the DMARC test, the receiving server looks at the domain's DMARC record to help it decide what to do with the failed email. The policy tag in that record tells it to either do nothing, quarantine the email, or reject it completely. It may also ignore the tag completely and do its own thing, but that particular behaviour is in the minority.
If there is a reporting address in the DMARC record, the receiving server also sends a report of the email transaction as an aggregate (rua) DMARC report.
What does a DMARC record look like?
Let's look at the DMARC record for Stripe for example. You can see it for yourself by doing a standard DNS lookup of
v=DMARC1; p=reject; rua=mailto:firstname.lastname@example.org; ruf=mailto:email@example.com
We'll break down each component in this record.
This is the DMARC version and serves as an identifier to receiving servers. It's a required tag, and if it's missing or incorrect, the DMARC test will be skipped.
Stripe has decided to implement a reject policy, instructing receiving servers to reject all emails that fail DMARC. Some servers may ignore this instruction, but most will follow it.
Email providers send aggregated reports to this address about all emails they've received from Stripe's domain. The reports are high-level and anonymized, containing no personally identifiable information.
For privacy reasons, most DMARC monitoring tools (including SendForensics) will only accept
Stripe also has a different email address for forensic reports. These are individual forensic failure reports that will be sent in real-time, including specific details about the failure.
Optional DMARC tags
There's a number of optional DMARC tags you can use to fine-tune your policy.
|pct||The percentage of failed emails that the set policy should apply to. For example, if you choose to ‘quarantine’ 10% of emails, the other 90% would be treated as though a ‘none’ policy was applied. The value should be a number between 1 and 100.|
|sp||The specific policy for emails sent from subdomains. You could choose to ignore failed emails sent from the main domain (p=’none’) but quarantine those sent from subdomains (sp=’quarantine’). Subdomains with their own separate DMARC record are not affected.|
|adkim||Decide how strict DMARC should be when comparing the sender’s domain against DKIM’s ‘d’ tag. Possible options are ‘strict’ and ‘relaxed’. By default, the approach is ‘relaxed’. Typically, 'strict' means that subdomain matches are not accepted.|
|aspf||The same choice, but for SPF alignment. Decide whether SPF should aim for a perfect match of “envelope from’ domain and “return-path” address or if subdomains of “envelope from” domain should be also allowed. Again, you can choose between ‘strict’ or ‘relaxed’.|
|ri||Sets the intervals for how often you want to receive aggregate reports (‘rua’ tag). The value is expressed in seconds, by default it’s 86400 (every 24 hours). Report-sending ISPs can however set their own sending schedule.|
|fo||Choose when to receive forensics reports (‘ruf’). Possible options are ‘0’ (SPF and DKIM failed), ‘1’ (SPF, DKIM, or DMARC failed), ‘d’ (DKIM failed), and ‘s’ (SPF failed). By default, it’s ‘0’.|
Stripe's full DMARC policy contains two of these optional tags:
pct=100, meaning 100% of failing emails should be rejected (the default setting i.e. this tag can also be left out of the record), and
fo=1, meaning to send a forensic report if any test (SPF, DKIM, or DMARC) fails.
`v=DMARC1; p=reject; pct=100; fo=1; rua=mailto:firstname.lastname@example.org; ruf=mailto:email@example.com`
Here's another example to understand the different tags.
Unlike Stripe, Amazon instructs servers to quarantine emails and send them to spam. This applies to 100% of emails. They also have both a
rua address and a
How to read DMARC reports
Securing emails with DMARC is only half the battle. The other half is analyzing the data in the reports you get back.
Raw DMARC reports contain report metadata and at least one record. Unfortunately, they're also in XML format. I'm really sorry. I didn't come up with it.
Here's an example aggregate report:
<?xml version="1.0" encoding="UTF-8" ?> <feedback> <report_metadata> <org_name>solarmora.com</org_name> <email>firstname.lastname@example.org</email> <report_id>9391651994964116463</report_id> <date_range> <begin>1335571200</begin> <end>1335657599</end> </date_range> </report_metadata> <policy_published> <domain>mydomain.com</domain> <adkim>r</adkim> <aspf>r</aspf> <p>none</p> <sp>none</sp> <pct>100</pct> </policy_published> <record> <row> <source_ip>203.0.113.209</source_ip> <count>2</count> <policy_evaluated> <disposition>none</disposition> <dkim>fail</dkim> <spf>pass</spf> </policy_evaluated> </row> <identifiers> <header_from>mydomain.com</header_from> </identifiers> <auth_results> <dkim> <domain>mydomain.com</domain> <result>fail</result> <human_result></human_result> </dkim> <spf> <domain>mydomain.com</domain> <result>pass</result> </spf> </auth_results> </record> </feedback>
Let's break this down.
<report_metadata> <org_name>solarmora.com</org_name> <email>email@example.com</email> <report_id>9391651994964116463</report_id> <date_range> <begin>1335571200</begin> <end>1335657599</end> </date_range> </report_metadata>
This is the metadata of the report. The
<org_name> contains the name of the ISP that received the email and is sending the DMARC report. The date ranges are in Unix time — use this converter to see the "human" date.
<policy_published> <domain>mydomain.com</domain> <adkim>r</adkim> <aspf>r</aspf> <p>quarantine</p> <sp>none</sp> <pct>100</pct> </policy_published>
This is the DMARC policy your domain is using at the time of the report. If you've recently changed your DMARC policy, this is useful for filtering reports.
<record> <row> <source_ip>203.0.113.209</source_ip> <count>2</count> <policy_evaluated> <disposition>none</disposition> <dkim>fail</dkim> <spf>pass</spf> </policy_evaluated> </row> <identifiers> <header_from>mydomain.com</header_from> </identifiers> <auth_results> <dkim> <domain>mydomain.com</domain> <result>fail</result> <human_result></human_result> </dkim> <spf> <domain>mydomain.com</domain> <result>pass</result> </spf> </auth_results> </record>
Now here's the fun bit: the
<record> and its
<auth_results>. This record shows the results for 2 emails received from 'mydomain.com'. DKIM has failed, but SPF is passing and the SPF domain is aligned with the
<header_from> domain (the sending-domain) so DMARC is passing.
If you send more than a few emails, you'll probably want an easier way to see your DMARC results than in raw XML. You can import it into Microsoft Excel, or use a tool like SendForensics to process it for you.
- DMARC needs either SPF or DKIM to work, but you should use both for better email security, redundancy and, ultimately, deliverability.
- There are two types of DMARC reports: aggregate (rua) containing all sending data, and forensic (ruf) representing individual emails. ISPs are more reluctant to send ruf reports in this new era of GDPR, CCPA et al, given the PII (personally identifiable information) they can contain. However, most support aggregate reports.
- DMARC aggregate reports are in XML format. You'll need to convert them to a readable format.