DMARC allows you to disallow participating recipients to reject emails that spoof your domain and report these violations to you. You can even run it in reporting only mode if the board is too scared to “loose email delivery”.

If you are concerned with phishing and spoofing and would like to know who is sending emails under the identifiy of your domain(s) this article is for you! But beware DMARC will not magically solve all your email related problems. It can, however, add to your telemetry.

Just by setting up a SPF and a DMARC record you will receive reports about which IPs send emails as your domain. You can then go down the rabbit hole to figuring out which of these machines are yours and which aren’t… and eventually assess how big of a spoofing problem you have.

(This article does not focus on how to use DMARC as recipient nor on any implementation aspects beyond getting reports.)

What is DMARC?

tl;dr: Domain-based Message Authentication, Reporting and Conformance (DMARC) is an email authentication protocol that prevents unauthorized entities to spoof emails from a DMARC protected domain. It further enables reporting of policy violation (i.e. spoofing attempts) to the domain owner by recipients of spoofed email.


DMARC uses SPF and DKIM. So first a quick introduction to those:

SPF

tl;dr: Sender Policy Framework (SPF) is a TXT record in a domains DNS zone setting the policy for which IP addresses are allowed to send email for that domain.

A SPF record is as follows:

example.com. 86400 IN TXT "v=spf1 mx ip4:128.0.11.0/26 include:subdomain.example.org -all"

The v=spf1 mx ip4:128.0.11.0/26 include:_spf.example.org -all is the policy:

The full SPF RFC: https://tools.ietf.org/html/rfc7208

DKIM

tl;dr: DomainKeys Identified Mail (DKIM) is an email signing mechanism with the senders public key published in the DNS of the email sending domain.


DKIM only signs over some parts of the email. However, the RFC says that:

“The From header field MUST be signed”

– RFC6376 https://tools.ietf.org/html/rfc6376


This ensures that at the very least the From header field is signed. (But usually all headers plus the body are signed anyway.)

Why requiring singing the From header is important follows:

DMARC

DMARC now combines SPF and DKIM to authenticate legitimate email.

To see how DMARC does this we will look at an example email being send via SMTP. In the following lines starting with S denote communication by the server and C what the client sends:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
S: 220 spamtrap.invalid ESMTP
C: HELO spammer.invalid                   <-- MTA verifies via reverse IP lookup
S: 250 spamtrap.invalid
C: MAIL FROM:<alice@domain.tld>           <-- RFC5321.MailFrom  <--- governed by SPF
S: 250 2.1.0 Ok
C: RCPT TO:<bob@company.tld>
S: 250 2.1.5 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
C:         d=gmail.com; s=20161025;
C:         h=mime-version:from:date:message-id:subject:to;
C:         bh=GFOrCWTEu7KnAzxa5f+jRsPbA2HWWszHnEaaMNQyshU=;
C:         b=HzzNcsGTV7xkhyfznyWWcFXvH0tLy+2r1Rkir75hukvwngmzFXzITRTSn02cxDJdRg
[...]
C:          D4Wg==
C: From: Charles <charles@acme.tld>       <--- RFC5322.From aka Display From    \
C: To: Eve <eve@blah.tld>                                                       |
C: Subject: Important Documents                                                 |
C: Date: Tue, 13 Aug 2019 19:32:56 +0500                                        |
C:                                                                              |
C: Hello John,                                                                  |
C:                                                                              | signed
C: see: http://totatllynotmalware.evil/ransomware.exe                           | via
C:                                                                              | DKIM
C: Sincerely,                                                                   |
C:                                                                              |
C: David                                                                        |
C:                                                                              |
C: .                                                                            /
S: 250 2.0.0 Ok: queued as 4851E2097E78
C: QUIT
S: 221 2.0.0 Bye


SPF will ensure that the client is actually allowed to send from the domain it gives as the RFC5321.MailFrom (also known as the envelope-from) address in the MAIL FROM command in line 4.

It does so by comparing the clients connecting IP with the IPs listed in the claimed from domain’s (in this example domain.tld) SPF record. If the IP is not authorized to send the server evaluates the SPF policy (see SPF).


DKIM then ensures that the email data is signed. First, a DKIM signature is placed in the header of the email to be send (lines 10-16), which in this case is signed over the From, Date, Message-Id, Subject and To headers (h= parameter in line 12). It is signed by domain gmail.com (d= parameter in line 11). And with the key found in selector 20161025 (s= parameter in line 11).

The server will download the key used for signing by querying the TXT record 20161025._domainkey.gmail.com. This would contain:

20161025._domainkey.gmail.com. 29 IN TXT "k=rsa\; p=MIIB...QqR" "tqE....AQAB"

Then it verifies the signed hash over the signed fields. If this succeeds it verified that the domain named in the d= parameter has signed the message. Nothing more! The domain in the d= parameter can actually be different than the RFC5321.MailFrom or RFC5322.From domains (in line 4 and 17)!

This is where DMARC comes into play:

DMARC authenticates use of the RFC5322.From domain by requiring that it match (be aligned with) an Authenticated Identifier.

– RFC7489 https://tools.ietf.org/html/rfc7489

DMARC checks if the domain in the RFC5322.From header (line 17) aligns (i.e. is identical) to either the SPF authenticated RFC5321.MailFrom domain (line 4) or the valid DKIM signature domain.1

The RFC5322.From is also known as the Display From and it is formated as "Display Name" <user@domain> and it is usually displayed in MUAs. It can be set independently from the RFC5321.MailFrom used in the envelope (hence also sometimes called envelope-from). The RFC5321.MailFrom is usually not displayed in MUAs.



The mail processed with and with added headers from OpenDKIM and python-policyd-spf looks as follows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

Lines 4-5 are the SPF results and lines 6-7 the DKIM results.

In the example the domain domain.tld did not contain a SPF record.

If Company.tld had set SPF correctly on their domain company.tld the above example would read

4
5
Received-SPF: Fail (mailfrom) identity=mailfrom; client-ip=42.42.42.42;
    helo=spammer.invalid; envelope-from=alice@domain.tld; receiver=<UNKNOWN>

because obviously they would not include the spammers IP 42.42.42.42 as an allowed sender.

Similarly, if a DKIM signature was missing from the email the server could not check the email and hence the Authenticated-Results line would be missing (line 6-7).

DMARC validation

Only iff:

OR

will DMARC validate.

DMARC policy

The DMARC policy, i.e. whether email that does not validate gets rejected or quarantined or can still pass normally, is controlled via a DNS TXT record under the _dmarc subdomain. So e.g. you have example.com a DMARC record would be:

_dmarc.example.com. 360 IN TXT "v=DMARC; p=reject; rua=mailto:dmarc@example.org;"

This record set the policy p= to reject this means mail not passing DMARC will be rejected. Possible policies are:

Another aspect here is the rua= option. It sets an email address to which (daily2) aggregated feedback reports should be send.

NOTE: One pitfall is when the domain of the email that should receive the reports is not the same as the domain of the DMARC record the receiving domain needs a special DNS TXT record allowing DMARC reports for the other domain to be send.

This is to prevent causing spam by sending reports to a domain that does not expect them.

In our example the email dmarc@example.ORG should receive the reports of example.COM. To this end, you need to add the following record to example.ORG:

example.com._report._dmarc.example.org. IN TXT "v=DMARC1;"

Full RFC for DMARC: https://tools.ietf.org/html/rfc7489

Reporting

If you setup a DMARC record with a valid email address in the rua= field you should eventually receive emails like:

Subject: Report domain: example.com Submitter: google.com Report-ID: 748039912866467146
From: noreply-dmarc-support@google.com
To: dmarc@example.com
Content-Type: application/zip; 
    name="google.com!example.com!1563148800!1563235199.zip"
Content-Disposition: attachment; 
    filename="google.com!example.com!1563148800!1563235199.zip"
Content-Transfer-Encoding: base64

Domains I regularity receive reports from are: - google.com - yahoo.com - aol.com

Unfortunately, not more providers par take in DMARC reporting.

Report

The attached report will look as follows and not only include data of failed DMARC validations but also successful validations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?xml version="1.0" encoding="UTF-8" ?>
<feedback>
  <report_metadata>
    <org_name>google.com</org_name>
    <email>noreply-dmarc-support@google.com</email>
    <extra_contact_info>https://support.google.com/a/answer/2466580</extra_contact_info>
    <report_id>748039912866467146</report_id>
    <date_range>
      <begin>1563148800</begin>
      <end>1563235199</end>
    </date_range>
  </report_metadata>
  <policy_published>
    <domain>example.com</domain>
    <adkim>r</adkim>
    <aspf>r</aspf>
    <p>none</p>
    <sp>none</sp>
    <pct>100</pct>
  </policy_published>
  <record>
    <row>
      <source_ip>42.42.42.42</source_ip>
      <count>1</count>
      <policy_evaluated>
        <disposition>none</disposition>
        <dkim>fail</dkim>
        <spf>fail</spf>
      </policy_evaluated>
    </row>
    <identifiers>
      <header_from>example.com</header_from>
    </identifiers>
    <auth_results>
      <spf>
        <domain>example.com</domain>
        <result>fail</result>
      </spf>
    </auth_results>
  </record>
  <record>
    <row>
      <source_ip>13.13.13.13</source_ip>
      <count>23</count>
      <policy_evaluated>
        <disposition>none</disposition>
        <dkim>pass</dkim>
        <spf>pass</spf>
      </policy_evaluated>
    </row>
    <identifiers>
      <header_from>example.com</header_from>
    </identifiers>
    <auth_results>
      <dkim>
        <domain>example.com</domain>
        <result>pass</result>
        <selector>20190715</selector>
      </dkim>
      <spf>
        <domain>example.com</domain>
        <result>pass</result>
      </spf>
    </auth_results>
  </record>
</feedback>

Lines 3-12 give you metadata, such as:

Lines 13-20 your published DMARC policy as seen by the organization (during the reporting timespan). Obviously DNSSEC will ensure that everyone can verify the authenticity of your published policy (but DNSSEC is not required).

In line 17 you can also clearly see that the p= parameter, i.e. the policy, is set to none but we still receive reports.

Line 21-65 are the report records:

Here we have one spoofed email (line 23-40). This spoofed email was send from 42.42.42.42 (line 23) and did not contain any DKIM records and the SPF validation failed (line 37).

We also have 23 (line 44) legitimate emails (line 41-65) send from 13.13.13.13 (line 43) with valid SPF and DKIM (line 55-59) authentication result.

None of the emails was rejected. This can be seen in lines 26 and 46. Only if we set our policy to reject would line 26 read <disposition>reject</disposition> and indicate that this spoofed email was rejected.


As you can see analyzing the report is straight forward.

Analytics

If you can not read an XML file or your reports are just way too big and require data analytics you can buy DMARC analytic services. For example, Twitter and Facebook use Agari, while Google instructs to send the reports directly to a Google owned email address, and hence probably use internal tools.

DMARC and privacy

Forensic reporting (requested via the ruf tag in the DMARC record) will include message details in these forensic reports.

So you should handle these forensic reports (should you every receive some) with care, as these could include legitimate email from your organisation, that (for whatever reason) failed the DMARC check.

On the other hand these forensic reports (which are supposed to be send in realtime) are highly valuable as they can contain From and Subject fields as well as parts of the email’s Body.

NOTE: Unfortunately, it seems that no one is actually sending these forensic reports. At least I have never received any forensic reports.

Random rant

While I do understand that in large organizations it may be difficult to determine what IPs should be allowed to send email, to form a valid SPF record. Also I understand that preventing to loose email deliverability is number 1 priority in many organizations.

But as outlined previously, setting up reporting only requires one DNS TXT record to be added to the domain. I consider this to be free.

While the benefits may be marginal: You receive a couple of reports that you need to analyze to get any useful information out of them. Not taking this free tailored threat intelligence is just absurd!

So please take this rant part with a grain of salt, but:

Not consuming DMARC reports is answering the question of “Do you want to know who is stealing your identify online / pretending to be you?” with “No.”

Non-sending domains

But I don’t send emails from that domain.

– Excuse #1 for people to not set SPF and DMARC DNS records.


This excuse is also used by big players you’d assume should know better such as the @certbund (the federal CERT of Germany):

Tweet from @certbund (the federal CERT of Germany)

Tweet from @certbund (the federal CERT of Germany)


Instead of excuses here is what you should do:

If you have domain (nomail.com) you don’t send mail from set the following records:

SPF:

nomail.com.        IN  TXT  "v=spf1 -all"

The setup a DMARC record that instructs to reject all mail that fails to validate and while you are at it include a request to send those rejection reports to your domain yesmail.com where you do emails on:

DMARC with reporting:

_dmarc.nomail.com. IN  TXT  "v=DMARC1; p=reject; rua=mailto:dmarc-23@yesmail.com; ruf=mailto:dmarc-reports-23@yesmail.com"

In the yesmail.com domain you need to allow DMARC reports for nomail.com to be send via the following DNS TXT record:

nomail.com._report._dmarc.yesmail.com. IN TXT "v=DMARC1;"

So you see that even if you don’t send emails you can still request to receive DMARC reports, .e.g. in this case to your yesmail.com domain. This is free threat intelligence you should take. Even if you only look into one report once every few month. It tells you what IPs violate your DMARC policy.

You not only get free threat intelligence, but also help recipients to better protect against spam spoofing your domain! So please be nice and set SPF records on your non email sending domains! Thank you.

Deployment statistics

So who is already using DMARC?

Scan of Alexa Top 14k

I scanned the first 14,794 domains of the Alexa Top 1M for the availability of various security features and policies that can be set via DNS records.


The data was obtained 2019-08-13. The data is not updated and reflects the state on that day.


You can search the results of the scan here:



Domain MX SPF Sender ID HIBP DKIM DMARC ASDP MTA-STS CAA DNSSEC TLSA
policy rua ruf issue iodef valid DNSKEY
Domain MX SPF Sender ID HIBP DKIM DMARC ASDP MTA-STS CAA DNSSEC TLSA
policy rua ruf issue iodef valid DNSKEY



You can also filter for policies, e.g. p=reject will filter for all entries with a reject DMARC policy set.

You can combine searches, e.g. searching for “-all dkim p=reject rua ruf issue dnssec” will filter for all domains that have a SPF fail policy, DKIM, a DMARC reject policy with aggregate and forensic reporting, a CAA issue policy, and valid verifiable signed DNSSEC.


The listed entries are:

Deployment statistics

Here are some over all numbers of the dataset:

TOTAL:                               14794
TOTAL: NO MX record                  2432 = 16 %

SPF:  NO POLICY                      5026 = 33 %
SPF:  all                            9768 = 66 %
SPF: -all                            3220 = 21 %
SPF: ~all                            5526 = 37 %
SPF: ?all                            579 = 3 %
SPF: +all                            9 = 0 %
SPF: illegal (multiple all tags)     173 = 1 %
(missing % use a pure redirect policy)

SPF:  NO POLICY while no MX          2130 = 87 % of domains without MX record
SPF:  -all while no MX               159 = 6 % of domains without MX record

DKIM: NO _domainkey.                 6969 = 47 %
DKIM: YES a _domainkey. exists       7825 = 52 %
(missing % are rounding errors)

DMARC: NO POLICY                     11062 = 74 %
DMARC: p=                            3732 = 25 %
DMARC: p=none                        2195 = 14 %
DMARC: p=reject                      958 = 6 %
DMARC: p=quarantine                  577 = 3 %
DMARC: rua                           3191 = 21 %
DMARC: ruf                           1907 = 12 %
DMARC: illegal (contradicting)       1 = 0 %

DMARC: p=none                        2195 = 58 % of domains with DMARC
DMARC: p=reject                      958 = 25 % of domains with DMARC
DMARC: p=quarantine                  577 = 15 % of domains with DMARC
DMARC: rua                           3191 = 85 % of domains with DMARC
DMARC: ruf                           1907 = 51 % of domains with DNARC
DMARC: illegal (contradicting)       1 = 0 % of domains with DNARC

ASDP: NO POLICY                      14546 = 98 %

MTA-STS:                             17 = 0 %

CAA: issue                           710 = 4 %
CAA: iodef                           230 = 1 %

DNSSEC:                              482 = 3 %
DNSKEY:                              886 = 5 %

TLSA: port 25/tcp or 443/tcp         45 = 0 %
TLSA: but no DNSSEC                  18 = 0 %
TLSA: but not even DNSKEY            18 = 0 %


What makes my sad about these numbers is:

What makes me facepalm:

What I can related to:

What give me hope:


In case you are wondering what the gradient is between top sites and lower Alexa ranked sites:

Top 100 100 - 200 200 - 300 300 - 400 400 - 500 500 - 1k 1k - 2k 2k - 3k 3k - 4k 4k - 5k 5k - 10k
DMARC 65% 66% 54% 37% 43% 38% 30% 30% 26% 27% 22%
SPF 89% 83% 77% 76% 76% 71% 72% 71% 68% 66% 64%
DKIM 68% 65% 66% 61% 60% 62% 56% 56% 54% 52% 51%

Here you can clearly see that there is a significant gradient to DMARC deployment. However, the SPF and DKIM gradient isn’t as harsh.

Future

A good introduction on DMARC is DMARC.org’s Introduction to Email Authentication presentation [1].

DMARC.org also has a lot of resources. The most interesting are:

SPF

Some resources to help with setting up an maintaining SPF records:

The Messaging, Malware and Mobile Anti-Abuse Working Group has a great best practice guide for managing SPF records [2].

Vladimir Dubrovin has a good article on the myths and legends of SPF [3].

DMARC

Hang Hu and Gang Wang of Virginia Tech scanned the SPF and DMARC records of the Alexa top 1 million domains [4]. They also checked for mail receivers DMARC policy regarding rejection of spoofed emails. They found that GMail (gmail.com) still lets around 53% of spoofed emails though despite not passing authentication checks. Only Hotmail (hotmail.com) rejected 100% of emails that did not pass authentication checks. T-Online (t-online.de) doesn’t even support any of SPF, DKIM nor DMARC and thus lets 100% of the spoofed emails pass without authentication checks.

So spam prevention happens at the receiver. Even if your company has a working DMARC policy if a receiver does not reject emails that don’t pass DMARC authentication there is nothing you can do about it.

On the other hand if you do not set a DMARC policy you are solely responsible when your domain gets spoofed and users can’t protect against it.


Hence at least enable DMARC reporting to know how bad the spoofing problem is for your organization. Then, if warranted, work towards a full DMARC p=reject policy.


References

[1] DMARC.org, “Introduction to Email Authentication - An explanation of how SPF, DKIM, and DMARC function.” https://dmarc.org/presentations/Email-Authentication-Basics-2015Q2.pdf, 2015.

[2] Messaging, Malware and Mobile Anti-Abuse Working Group, “M3AAWG best practices for managing sPF records.” https://www.m3aawg.org/sites/default/files/m3aawg_managing-spf_records-2017-08.pdf, August-2018.

[3] Vladimir Dubrovin, “Myths and Legends of SPF.” https://hackernoon.com/myths-and-legends-of-spf-d17919a9e817, October-2017.

[4] Hang Hu and Gang Wang, “End-to-End Measurements of Email Spoofing Attacks,” 27th USENIX security symposium (USENIX security 18). https://www.usenix.org/conference/usenixsecurity18/presentation/hu; USENIX Association, Baltimore, MD, pp. 1095–1112, Aug-2018.


  1. In case the policy is set to strict a domain must match exactly including all subdomains, while in relaxed mode it is enough for the root domain without any subdomains to match.

  2. While this should be adjustable via the ri= parameter, all providers will send daily reports.