DMARC Reports: Parse Aggregate Data and Find Alignment Failures
DMARC aggregate reports are receiver-generated summaries of messages claiming to use a domain. They reveal source IPs, message counts, authentication outcomes, alignment and policy disposition, but they do not contain message bodies or prove that every source is malicious or legitimate. Analysis should normalize reports, map sources to accountable services, investigate alignment failures and advance policy only after required mail streams are understood.
Publish reporting addresses and govern intake
A DMARC record can request aggregate reports with rua=mailto:[email protected]. Use a dedicated, secured mailbox or processor that can handle compressed XML and validate external report authorization when reports are sent to another organizational domain. Apply malware-safe decompression limits and retain original files with checksums.
Deduplicate by reporting organization, report ID and date range. Reporting coverage varies, so absence from reports does not prove absence of traffic.
Normalize the XML without losing provenance
report_metadata: org_name, report_id, begin, end
policy_published: domain, adkim, aspf, p, sp, pct
record.row: source_ip, count, disposition, dkim, spf, reason
identifiers: header_from, envelope_from, envelope_to
auth_results: dkim domain/selector/result; spf domain/scope/resultStore raw and parsed forms, parser version and validation errors. One row represents an aggregate count, not one message. Keep unknown values instead of coercing them to pass or fail.
Separate authentication from alignment
SPF can pass for an envelope domain yet fail DMARC when it is not aligned with the visible From domain. DKIM can pass for a vendor signature and still be unaligned. DMARC needs one aligned passing mechanism. Evaluate organizational-domain and strict/relaxed mode according to the published policy.
| SPF aligned | DKIM aligned | DMARC |
|---|---|---|
| Yes | No | Pass |
| No | Yes | Pass |
| No | No | Fail |
| Authentication error | No | Investigate failure/temperror context |
Map every material source to an owner
Group by source IP, reverse DNS, ASN, header From, authenticated domains and selector. Label as approved direct infrastructure, approved vendor, forwarding/intermediary, unauthorized, unknown or decommissioned. Require a service owner, business purpose, expected volume and remediation plan.
Do not allowlist an IP merely because volume is high. Shared cloud addresses can represent many tenants, and an attacker can send from infrastructure with good reputation. Authorization depends on the contracted sending route and aligned identity.
Investigate failure patterns
- SPF pass, DMARC fail: envelope domain likely unaligned; configure a custom return path or aligned DKIM.
- DKIM fail after forwarding: body/header modification may break the signature; inspect ARC and intermediary behavior.
- Unknown source, both fail: likely unauthorized use, but confirm internal and vendor inventories.
- Disposition none despite reject policy: examine receiver override reasons and report semantics.
- Sudden volume jump: check campaign, compromised system, new vendor or spoofing.
Advance policy using measured readiness
Begin with monitoring, correct legitimate streams, then use a controlled percentage or subdomain scope where appropriate. Before quarantine or reject, quantify aligned legitimate volume, unresolved high-value sources, report coverage and rollback criteria. Keep one aligned DKIM path resilient because forwarding can make SPF unreliable.
Enforcement blocks unauthorized direct use of the domain in From; it does not stop lookalike domains or every phishing technique. Continue brand monitoring and user protection.
Use the current DMARC and aggregate-report specifications
DMARC is now defined by RFC 9989, with aggregate reporting in RFC 9990 and failure reporting in RFC 9991. RFC 7489 is obsolete. Implementations and historical report generators can still reflect earlier behavior, so store report metadata and parser version rather than assuming every producer emits the same optional fields.
DMARC evaluates the visible RFC 5322 From domain and passes when at least one authenticated SPF or DKIM identifier aligns under policy. Aggregate reports summarize receiver observations by source and authentication disposition. They do not list inbox placement, individual complaints or every message.
Parse the DMARC DNS policy and reporting authorization correctly
_dmarc.example.com. 3600 IN TXT
"v=DMARC1; p=quarantine; rua=mailto:[email protected]; pct=100"
external report authorization example:
example.com._report._dmarc.reports.example.net. IN TXT "v=DMARC1"Publish one valid DMARC record at the correct owner name. Multiple records or malformed tags can make policy invalid. External reporting destinations require authorization under the reporting specification. Verify authoritative and recursive DNS, TXT concatenation, organizational-domain/subdomain behavior and mailbox/collector capacity.
Keep reporting addresses separate from personal mailboxes. Reports are untrusted attachments and can arrive compressed.
Treat aggregate reports as untrusted external files
| Risk | Control |
|---|---|
| Compression bomb | Compressed/expanded size, ratio and recursion limits |
| Malformed XML | Disable external entities/network access; schema-aware parser |
| Duplicate report | Reporter, report ID, date range and payload checksum |
| Oversized row count | Bounded streaming parser and quarantine |
| Forged reporter metadata | Retain transport evidence; do not trust XML contact as authentication |
| Unknown fields/enums | Preserve raw report and forward-compatible unknown state |
Parse in an isolated worker without shelling out to filenames. Store raw payload checksum, MIME metadata, parser version and quarantine reason under restricted access.
Normalize reports without losing policy and identifier context
dmarc_report(
reporter, report_id, begin_utc, end_utc,
policy_domain, adkim, aspf, p, sp, pct,
received_at_utc, raw_checksum, parser_version
)
dmarc_row(
report_key, source_ip, message_count, disposition,
dkim_eval, spf_eval, reason_type,
header_from, envelope_from, envelope_to
)
dmarc_auth_result(
row_key, mechanism, auth_domain, selector,
scope, result, human_result
)One row can contain several DKIM or SPF authentication results. Do not flatten them into a comma string. Retain policy-evaluated aligned results separately from mechanism results. Store IP in a type supporting IPv4/IPv6 and preserve reporter-specific unknown values.
Separate authentication pass from DMARC alignment
| SPF | DKIM | DMARC interpretation |
|---|---|---|
| Pass, aligned MAIL FROM | Any | Pass through SPF alignment |
| Pass, unaligned vendor domain | Pass, aligned d= | Pass through DKIM |
| Pass, unaligned | Pass, unaligned | Fail despite two mechanism passes |
| Fail after forwarding | Pass and aligned | Pass through DKIM |
| Fail | Fail/missing | Fail; disposition depends on policy/local handling |
Group by visible From, envelope domain, DKIM domain/selector, source IP and reporter. A vendor source can pass SPF but remain unauthorized for the brand’s DMARC identity.
Reconcile every observed source with an accountable sending service
Maintain authorized service, CIDR/IP, provider, stream, From domains, envelope domains, DKIM domains/selectors, owner and retirement date. Match report rows to the inventory with effective dates. “Known cloud provider” is not sufficient; identify the exact customer/service path.
| Source state | Action |
|---|---|
| Authorized and aligned | Monitor volume/authentication drift |
| Authorized but misaligned | Fix customer DKIM/return path before enforcement |
| Unknown low volume | Investigate, do not ignore by percentage |
| Former vendor still active | Stop credentials/routing and remove authorization safely |
| Forwarder/mailing list | Assess path, ARC and expected authentication behavior |
Track first/last seen and unexpected volume. An attacker or forgotten application can hide inside a 99% pass total.
Move policy toward enforcement with measured gates
- Inventory all legitimate sources and publish reporting.
- Establish stable report ingestion and source ownership.
- Correct aligned DKIM/SPF on every required route.
- Identify forwarded/list traffic and subdomain policy impact.
- Simulate proposed disposition by reporter/source.
- Increase enforcement scope under explicit business gates.
- Monitor bounces, support cases, aggregate reports and legitimate flows.
pct behavior and receiver local policy do not make partial rollout a perfect experiment. Preserve the published policy and effective time. Do not jump to rejection merely to improve a compliance dashboard while legitimate services still fail alignment.
Calculate report metrics with correct denominators
total_messages = SUM(row.message_count)
dmarc_pass = SUM(count WHERE policy_evaluated_dkim=pass
OR policy_evaluated_spf=pass)
pass_rate = dmarc_pass / total_messages
unknown_source_share = SUM(count WHERE source_owner IS NULL)
/ total_messagesSegment by reporter, source, identity and date. Reports cover intervals and may arrive late or overlap; deduplicate before summing. An aggregate row count is messages represented, not recipients or unique messages. Publish coverage reporters, arrival lag, invalid/quarantined reports and unknown source share.
Worked case: 99.8% pass hides an unauthorized billing application
A domain appears healthy at 99.8% DMARC pass. The remaining 0.2% comes from a cloud range and is dismissed. Source reconciliation shows the same IP sends monthly billing notices from a recently acquired subsidiary. SPF passes for the SaaS vendor but is unaligned; its DKIM also uses the vendor domain.
The service is legitimate but incorrectly configured. Before moving to p=reject, operators configure a customer-aligned DKIM domain, test final received messages and verify the source appears aligned across several reporters. They also assign an owner and credentials retirement process.
The incident demonstrates why percent alone is unsafe. A small stream can contain legally or operationally critical messages, while an unknown malicious source can also remain statistically small.
Operate a daily DMARC review and incident path
- Report intake success, duplicates, quarantine and arrival delay.
- Message volume/pass by reporter and source.
- Unknown/new IP, DKIM domain and selector.
- Authorized-source alignment regression.
- Policy/disposition changes and subdomain impact.
- Forwarding/list patterns and ARC evidence.
- Source owner, remediation ticket and verification date.
When a new source appears, preserve report rows, identify the application/credential and contain unauthorized sending. Do not add it to SPF before establishing ownership and purpose. Close after corrected/disabled behavior appears across matured reports.
Read the aggregate XML hierarchy before flattening it
<feedback>
<report_metadata>...report_id/date_range...</report_metadata>
<policy_published>
<domain>example.com</domain><adkim>r</adkim>
<aspf>r</aspf><p>reject</p><pct>100</pct>
</policy_published>
<record>
<row><source_ip>192.0.2.25</source_ip><count>840</count>
<policy_evaluated><disposition>none</disposition>
<dkim>pass</dkim><spf>fail</spf>
</policy_evaluated></row>
<identifiers><header_from>example.com</header_from></identifiers>
<auth_results>...one or more DKIM/SPF results...</auth_results>
</record>
</feedback>The policy-evaluated results express aligned DMARC evaluation. Authentication results can include several signatures or SPF identities and are not interchangeable. A disposition of none does not always mean published p=none; local policy, overrides and evaluation reasons matter.
Interpret disposition and policy override reasons cautiously
| Observation | Meaning | Action |
|---|---|---|
| DMARC fail, disposition reject | Receiver reports enforcement consistent with policy/local handling | Identify source and legitimate impact |
| DMARC fail, disposition none | Override/local policy or non-enforcement context | Inspect reason fields and reporter behavior |
| DMARC pass, disposition none | Normal accepted authentication result | Monitor source ownership |
| Forwarded/mailing-list reason | Receiver reports an override rationale | Assess real path and ARC; do not globally allow |
Reason type/comment is reporter-provided context, not a universal guarantee. Preserve unknown reasons. Policy disposition is not inbox placement and a rejected row does not identify every affected recipient.
Model organizational and subdomain policy boundaries
The visible From may be an organizational domain or a subdomain with its own DMARC record/policy behavior. Maintain an effective-policy resolver based on the current specification and Public Suffix List behavior rather than splitting on the last two labels. Internationalized domains require consistent A-label/U-label handling.
effective_policy(
header_from_domain, organizational_domain,
discovered_policy_domain, applied_p_or_sp,
adkim, aspf, pct, discovery_version,
public_suffix_data_version
)Test exact-domain records, inherited subdomain behavior, delegated business units and non-existent names. Publishing sp=reject can affect forgotten subdomains and third-party services. Inventory them before enforcement.
Reconcile reports across receivers without double counting
Each reporter observes its own population, time boundary and receiver behavior. Deduplicate within reporter/report ID/date range, then aggregate only nonoverlapping records. Compare report message counts directionally with outbound accepted traffic by destination, recognizing forwarded mail, receiver grouping and reporting coverage.
| Difference | Possible cause |
|---|---|
| Outbound volume greater | Nonreporting receivers, report lag or different time boundary |
| Report volume greater | Forwarded/resent traffic, overlap or unauthorized source |
| One reporter missing | Collector failure, no eligible traffic or reporter delay |
| Sudden new source at several reporters | New service, compromise or shared route change |
Do not scale reports to force equality. Publish a coverage inventory and freshness state.
Investigate an unknown source before authorizing it
- Preserve report rows, source IP, identities, selectors, reporters and dates.
- Check current and historical route/service inventory.
- Search application, cloud, ESP and security logs for the source.
- Determine whether it is forwarding, a forgotten vendor or unauthorized sending.
- Contain compromised credentials or infrastructure before DNS changes.
- For legitimate mail, configure aligned identity and test final artifacts.
- For unauthorized mail, let policy operate and monitor disappearance.
Adding an unknown IP to SPF can legitimize abuse and consume lookup budget. Establish ownership and envelope use first. A cloud netblock does not identify the tenant.
Test parser, policy and reports before an enforcement change
Create sanitized XML fixtures for IPv4/IPv6, multiple auth results, unknown reason, missing optional fields, external reporting, duplicate IDs, overlapping intervals and malformed/compressed payloads. Assert message counts, alignment, disposition and source ownership.
Before DNS release, send through every legitimate route to controlled receivers, inspect final Authentication-Results, and verify aggregate reports after their normal delay. Record policy TXT, TTL, effective organizational/subdomain behavior, report collector health and rollback condition. DNS rollback is not instant because caches persist.
After change, monitor legitimate bounce/support cases and report dispositions. Close only when required sources align across matured reporters and unknown traffic remains contained.
Monitor report-pipeline quality as a security control
Track expected reporters, arrival lag distribution, messages represented, duplicate rate, parser rejection, unknown enum/field, external authorization failure and storage freshness. A broken collector can make unauthorized traffic disappear from dashboards. Missing reports are unknown coverage, not zero failures.
| Alert | First check |
|---|---|
| All reporters stop | Mailbox/HTTP collector, DNS authorization and parser |
| One material reporter stops | Traffic change, reporter delay and intake filters |
| Message count collapses | Sending volume/identity change or dropped rows |
| Unknown values appear | Specification/vendor change; preserve and update parser |
| Duplicates spike | Idempotency key and sender retries |
Keep raw payloads long enough to replay after parser correction under privacy and storage policy.
Minimize data while keeping source accountability
Aggregate reports contain domains, IP addresses and operational metadata. Restrict source-level access, define retention and avoid joining recipient-level marketing data because aggregate reports do not require it. Reporting addresses and XML comments can contain unexpected text; escape all presentation.
Use pseudonymous incident identifiers in broad dashboards and keep raw source ownership in a controlled inventory. When sharing examples publicly, replace domains, IPs, report IDs and timestamps consistently. Do not upload proprietary reports to public analyzers without authorization.
Parser/analyst access should be logged. External report destinations need vendor security and deletion review.
Simulate policy effect before changing DNS
for each normalized row:
effective_policy = resolve_policy(header_from, published_snapshot)
aligned_pass = aligned_spf_pass OR aligned_dkim_pass
proposed_action = aligned_pass ? "pass" : proposed_policy
aggregate count by source_owner, reporter, stream, proposed_action
Simulation estimates report-row impact; it does not force receiver behavior. Include policy overrides, subdomain rules, pct limits and unknown sources separately. Review critical legitimate flows by owner and obtain final received samples.
Record the exact dataset/date range and proposed DNS artifact. Repeat immediately before release if sources changed. A month-old simulation is unsafe for fast-changing SaaS inventories.
Report authentication risk without hiding small sources
Show total represented messages, aligned pass, authorized misaligned, unknown/unauthorized, reporter coverage and enforcement disposition. Include counts beside percentages. Rank unknown sources by risk and owner status, not only volume. A ten-message payroll source can matter more than a large test stream.
Separate control health from policy strength: a domain at p=reject with broken ingestion is not better operated than a monitored domain correcting its last legitimate sources. Publish next action, owner and verification date for every material exception.
Detect selector and service drift inside passing domains
Track DKIM selector, signing domain and source together. A new selector may be a planned rotation, new vendor, regional route or unauthorized service. Verify it against the source inventory and final message. An old selector continuing after retirement can indicate delayed queues or credentials that were never disabled.
| Pattern | Action |
|---|---|
| New selector on known source | Confirm approved rotation and key strength |
| Known selector on unknown IP | Investigate key/credential use and vendor routing |
| Old selector persists | Check queues, integrations and retirement plan |
| Aligned pass moves to vendor signature | Ensure customer identity was not lost |
Selector inventory helps distinguish DNS/key incidents from audience and reputation problems.
DMARC reporting production checklist
- Use RFC 9989 with RFC 9990 aggregate reporting.
- Publish one valid policy record and external authorization.
- Harden compressed XML ingestion.
- Deduplicate and retain raw checksum/parser version.
- Separate policy evaluation from authentication results.
- Map every source, identity and selector to an owner.
- Show counts, coverage, lag and unknowns.
- Simulate policy changes on current data.
- Test every legitimate route before enforcement.
- Investigate small unknown sources rather than hiding them in pass rate.
Review after every ESP, DNS, selector, From-domain or acquisition change.
Worked security case: a valid vendor route is abused
Aggregate reports show a known aligned DKIM selector sending ten times its normal volume from the expected vendor range. DMARC passes, so a pass-only dashboard stays green. Internal campaign records contain no matching launch.
The team treats authentication as identity evidence, not authorization for the activity. It revokes the exposed API credential, holds the vendor account, preserves submission logs and suppresses attacker-generated queues. Provider/source volumes return to baseline in matured reports. A new alert compares authenticated source volume with approved campaign and credential activity.
This case proves why DMARC pass does not mean wanted, benign or authorized content. Report analysis must combine authentication with operational ownership and expected traffic.
Record standards and data review
Record the RFC versions, parser release, Public Suffix data version, report coverage window and responsible reviewer. Revalidate after standards, DNS, provider or service inventory changes.
Approval record
Retain the final policy approval, DNS artifact, owner, UTC release time and rollback condition with the reporting evidence.
Primary references
- DMARC.org overview
- RFC 6376 DKIM
- RFC 9989: DMARC
- RFC 9990: DMARC Aggregate Reporting
- RFC 9991: DMARC Failure Reporting


