Tools · Email security · 05 / 18
Reading DMARC aggregate reports to find every sender using your domain
Parse DMARC rua XML reports, group results by source IP and alignment, identify legitimate senders that need fixing, and spot spoofing attempts.
What an aggregate report contains
Receivers that honor rua send daily XML files, usually compressed, listing for each source IP the message count, the DKIM and SPF results, the domains used, and the policy applied. They contain no message content. The volume quickly outgrows reading by hand, so parse them into a table.
<record>
<row><source_ip>198.51.100.25</source_ip><count>412</count>
<policy_evaluated><disposition>none</disposition><dkim>fail</dkim><spf>pass</spf></policy_evaluated></row>
<identifiers><header_from>example.org</header_from></identifiers>
<auth_results><dkim><domain>vendor.example</domain><result>pass</result></dkim>
<spf><domain>example.org</domain><result>pass</result></spf></auth_results>
</record>A small parser
Python’s standard library is enough to flatten reports into CSV. Decompress the files first; most arrive as .zip or .gz attachments.
import csv, glob, sys, xml.etree.ElementTree as ET
w = csv.writer(sys.stdout)
w.writerow(['org', 'source_ip', 'count', 'header_from', 'dkim', 'spf', 'disposition'])
for path in glob.glob('reports/*.xml'):
root = ET.parse(path).getroot()
org = root.findtext('report_metadata/org_name')
for rec in root.iter('record'):
pe = rec.find('row/policy_evaluated')
w.writerow([org, rec.findtext('row/source_ip'), rec.findtext('row/count'),
rec.findtext('identifiers/header_from'),
pe.findtext('dkim'), pe.findtext('spf'), pe.findtext('disposition')])Lab: classify sources
Run the parser over a week of reports for your domain, or over the sample reports published by open-source DMARC tools. Group by source IP and look up each IP’s owner with reverse DNS or WHOIS. Classify each source as your platform, a known vendor that needs alignment fixes, forwarding, or unknown. Unknown sources failing both checks are likely spoofing and will be blocked once you reach reject.
python3 parse_dmarc.py > dmarc.csv cut -d, -f2 dmarc.csv | sort | uniq -c | sort -rn | head dig +short -x 198.51.100.25 whois 198.51.100.25 | grep -iE 'orgname|netname'
At scale
Open-source tools such as parsedmarc ingest reports from a mailbox into Elasticsearch or Splunk with dashboards, and many commercial services do the same. Whatever the tool, the questions stay the same: who sends as us, do they align, and what fails.
Diagram
Typical source classes in aggregate reports
Each class leads to a different action.
- Your mail platform
Should pass and align; investigate any failures.
- Known vendor
Passes but does not align; configure custom DKIM.
- Forwarder or list
SPF fails after forwarding; DKIM may survive; consider ARC.
- Unknown
Fails both; likely spoofing that enforcement will block.
Check yourself
1. Do aggregate reports include message content?
2. A vendor’s IP passes SPF and DKIM but neither aligns. What is it?
3. An unknown IP fails both checks with your From domain. What does it likely indicate?