p=none is not monitoring

2026-07-29

Most domains that have a DMARC record have this one:

v=DMARC1; p=none;

That is monitoring mode. It asks receivers to apply no policy and, because there is no rua= tag, to send the results nowhere. It is monitoring with nothing monitoring — a record that produces no enforcement and no data, indefinitely.

It is worse than having no record at all, and not for a technical reason. A domain with no DMARC record is visibly unfinished. A domain with p=none and no reporting address looks like a control. It will be ticked off in a questionnaire, listed in a risk register, and cited in a supplier security review, and every one of those is wrong.

The fix is not to jump to p=reject. It is to add the rua= tag, because you cannot make an enforcement decision without knowing who is sending as you.

What DMARC actually checks

The most common misunderstanding about DMARC is that it requires SPF and DKIM to pass. It does not. DMARC is an OR, not an AND.

A message passes DMARC if either of these holds:

  • SPF passes and the domain SPF authenticated is aligned with the domain in the From header, or
  • DKIM verifies and the signing domain is aligned with the domain in the From header.

Either one is enough. Enforcement — quarantine or reject — applies only to mail that fails both.

The alignment part is the part that gets skipped in summaries and it is the part that matters. SPF authenticates the envelope sender, which the recipient never sees; DKIM authenticates whatever domain signed the message. DMARC's contribution is to insist that one of those be the domain the reader actually sees in the From line. Without alignment, SPF and DKIM tell you a message was sent legitimately by someone — not that it was sent legitimately by you.

The OR is deliberate, and the reason is forwarding. When a mailing list or a forwarding rule relays your message, the sending IP changes — so SPF, which is bound to the IP, breaks. The DKIM signature travels with the message body and headers and survives. Had DMARC been an AND, ordinary forwarding would fail every message under a strict policy, and DMARC would have been undeployable.

What a passing row looks like

This domain publishes an enforcing policy:

_dmarc.viktorsundberg.me.  TXT  "v=DMARC1; p=quarantine; rua=mailto:postmaster@viktorsundberg.me"
viktorsundberg.me.         TXT  "v=spf1 ip4:<redacted> ip6:<redacted> ~all"

The aggregate reports come back as gzipped XML attachments to postmaster@, which is unreadable by hand, so a timer parses them weekly and mails me a plain-text digest.1

The row worth understanding is the one that looks like a failure and is not. Take a message that originates on my own mail server, is DKIM-signed with my own selector, and is then relayed onward through somebody else's outbound infrastructure before reaching its final recipient. A forwarding rule is enough to produce it. The report shows:

Field Value
SPF fail — the relay is not in my SPF record
DKIM pass — signing domain viktorsundberg.me, aligned
DMARC pass
Disposition none

SPF failed, and it was always going to: my SPF record lists my server's addresses, and the message did not arrive from one of them. The DKIM signature was intact and aligned, the OR was satisfied, and the message was delivered — under p=quarantine, which is to say under an enforcing policy that had every opportunity to act and correctly did not.

Those four values are not a measurement. They follow necessarily from the OR, which is the point: that combination is what a working deployment produces, every time, for every forwarded message. It is also easy to read as a failure, escalate, and start "fixing" a forwarding path that was never broken.

The pre-flight that matters

The mail that breaks when you go to p=reject is not the attacker's. It is legitimate mail from senders you have forgotten you authorised.

In a real rollout the surprises are structural rather than exotic:

  • A transactional email provider sending order confirmations, onboarding mail, or password resets as your domain — configured years ago by someone who has since left.
  • A portal or SaaS product signing its own outbound mail on your behalf, with its own selector, which nobody added to your records.
  • A tenant or domain verification record left in place from a migration, still authorising a sender that no longer needs it.

None of these is discoverable by asking around. They are discoverable from aggregate reports, which is the actual reason to publish rua= before you publish an enforcing policy. Watch until sources stop surprising you, fix the ones that should pass, then move the policy.

Going p=nonep=quarantinep=reject, with pct= to ramp if the domain is large, is the conventional path, and it is conventional because it works.

The part to be honest about

p=reject stops one attack: mail with your exact domain in the From header, sent by someone who cannot sign as you. That is a real attack and worth stopping.

It does not stop:

  • A lookalike domain. Your domain with a hyphen inserted, a different TLD, or a letter swapped. It is a different domain, it is authenticated correctly, and it passes its own DMARC.
  • Display-name forgery. A free mailbox with your CEO's name in the display name. The From domain is the free provider's; DMARC has no opinion, because nothing is being spoofed at the domain level.

This matters most in the case where DMARC gets deployed fastest: in response to a phishing incident. Deploy it — it is the right thing to do, and the incident is a good reason to finally do the work. But do not write it into the incident record as the control that closed the hole, because the message that got through probably did not depend on exact-domain spoofing at all. Multi-factor authentication and user training close that one. DMARC closes the neighbouring one.

The cheap remaining items

Three things sit next to DMARC, cost an afternoon, and are worth doing once you are enforcing:

  • MTA-STS, which tells sending servers to require TLS to your domain and refuse to fall back to plaintext.
  • TLS-RPT, which reports when they could not.
  • DNSSEC, which makes all of the above harder to strip in transit.

This domain publishes none of the three as of writing, which is the honest reason they are at the end of the essay rather than the middle.

Under ISO 27001 these are unusually good evidence. A Statement of Applicability entry for A.5.14 that points at a published, externally verifiable DNS record is a stronger artifact than a paragraph of policy describing an intention, because an auditor can check it from outside your network in about ten seconds.

Footnotes

  1. Python standard library only, run from a systemd timer, no third-party DMARC service involved. The reports are untrusted input — anyone can email one to postmaster@ — so the parser refuses any document declaring a DTD and caps decompression, which is what stops the XXE and entity-expansion attacks that DMARC parsers are a standing target for.