# DMARC policy at quarantine or reject in production

- **Pattern:** `ab-000927` (`deliverability-engineering.dns-auth.dmarc-policy`)
- **Severity:** critical
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000927
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000927)

## Why it matters

A DMARC record at `p=none` is monitoring mode only — it generates aggregate reports but does not instruct receiving mail servers to reject or quarantine spoofed email. Attackers can send phishing email appearing to come from your domain and it will land in inboxes unchallenged. RFC7489 requires `p=quarantine` or `p=reject` before DMARC provides any actual protection. Google's bulk sender guidelines for 2024 mandate DMARC enforcement for domains sending more than 5,000 messages per day; senders without enforcement face increasingly aggressive filtering at Gmail.

## Severity rationale

Critical because `p=none` provides zero spoofing protection — your domain can be impersonated in phishing campaigns with no technical barrier, and Gmail's bulk sender requirements mandate enforcement-level DMARC for high-volume senders.

## Remediation

Publish a DMARC record at `_dmarc.yourdomain.com` with `p=quarantine` or `p=reject`. Graduate from `p=none` only after reviewing aggregate reports to confirm SPF and DKIM are passing. Add programmatic validation to your health check or CI pipeline:

```ts
export async function validateDmarcRecord(sendingDomain: string): Promise<void> {
  const records = await dns.resolveTxt(`_dmarc.${sendingDomain}`)
  const rec = records.flat().find(r => r.startsWith('v=DMARC1'))
  if (!rec) throw new Error(`No DMARC record at _dmarc.${sendingDomain}`)
  if (rec.includes('p=none')) {
    throw new Error(`DMARC for ${sendingDomain} is monitor-only — not enforcing`)
  }
}
```

A production-ready record: `v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@yourdomain.com; pct=100; adkim=s; aspf=s`. Include both `rua` (aggregate) and `ruf` (forensic) reporting addresses and track these in infrastructure-as-code.

## Detection

- **ID:** `dmarc-policy`
- **Severity:** `critical`
- **What to look for:** Count all DMARC record definitions found in the codebase, infrastructure-as-code, DNS configuration scripts, or documentation. Look for `v=DMARC1` strings. Check the `p=` tag value — acceptable values are `quarantine` or `reject`. A `p=none` policy means DMARC is in monitor-only mode and provides no protection. Also check for DNS validation code that verifies the DMARC record is published. Check if the DMARC RUA (reporting URI) is configured to receive aggregate reports.
- **Pass criteria:** At least 1 DMARC record specifies `p=quarantine` or `p=reject` for production sending domains. A `p=none` policy does not count as pass. The record includes an `rua=` tag pointing to an address for aggregate reports. The policy is version-controlled or validated programmatically.
- **Fail criteria:** DMARC record specifies `p=none`, no DMARC record is defined, or no mechanism validates that DMARC is published correctly for production domains.
- **Skip (N/A) when:** The project does not send email.
- **Detail on fail:** Describe the gap. Example: `"DMARC policy is 'p=none' — email is not protected against spoofing, only monitored"` or `"No DMARC record defined or validated in codebase or infrastructure-as-code"`
- **Remediation:** Publish a DMARC record at `_dmarc.yourdomain.com`:

  ```dns
  v=DMARC1; p=quarantine; rua=mailto:dmarc-reports@yourdomain.com; ruf=mailto:dmarc-forensic@yourdomain.com; pct=100; adkim=s; aspf=s
  ```

  Graduate from `p=none` → `p=quarantine` → `p=reject` only after monitoring aggregate reports to confirm SPF and DKIM alignment. Validate DMARC publication programmatically:

  ```ts
  export async function validateDmarcRecord(sendingDomain: string): Promise<void> {
    const dmarcDomain = `_dmarc.${sendingDomain}`
    const records = await dns.resolveTxt(dmarcDomain)
    const dmarcRecord = records.flat().find(r => r.startsWith('v=DMARC1'))

    if (!dmarcRecord) {
      throw new Error(`No DMARC record found at ${dmarcDomain}`)
    }

    if (dmarcRecord.includes('p=none')) {
      console.warn(`DMARC for ${sendingDomain} is in monitor-only mode (p=none) — not enforcing`)
    }
  }
  ```

## External references

- external RFC7489 — https://www.rfc-editor.org/rfc/rfc7489
- cwe CWE-345
- iso-25010 reliability

Taxons: operational-readiness

HTML version: https://auditbuffet.com/patterns/ab-000927
