---
title: "Check Email Spoofing (DMARC SPF DKIM)"
canonical: https://trickest.com/library/email-spoofing-scanner-dmarc-spf-dkim
description: "Check whether an attacker can send mail as your domain by querying SPF, DKIM, DMARC, MTA-STS and BIMI across every subdomain."
---

# Check Email Spoofing (DMARC SPF DKIM)

## Overview

Email authentication is configured once, usually by whoever set up the mail
provider, and then inherited by every subdomain nobody thought about. The apex
has a strict DMARC policy. The subdomain used by a marketing tool three years ago
has none, and it is just as usable for sending mail that appears to come from you.

This checks the whole tree rather than the apex. You get every subdomain's SPF,
DKIM, DMARC, MTA-STS and BIMI state, resolved the way a receiving mail server
resolves it, and a spoofability verdict per host. The result is a list of the
names an attacker could send as today, which is the fix list. Because it is DNS
only, it is passive and cheap to repeat, so it belongs on a schedule next to the
rest of your [exposure management](/use-cases/exposure-management). Every
subdomain a team stands up arrives with whatever mail posture it inherited, and
the gap is worth catching before somebody else finds it.

No mail is sent at any point.

## Pipeline

1. Read the target domain.
2. [subfinder](/tools/subfinder) enumerates subdomains, because policy is
   inherited per name and gaps live below the apex.
3. The check list is built from the apex plus every discovered subdomain.
4. SPF, DKIM, DMARC, MTA-STS and BIMI records are queried over DNS for each name,
   distributed across the fleet, following the DMARC organizational tree-walk.
5. Each name is scored for spoofability from its resolved policy.
6. The exposure list is written to a PDF report.

## Inputs

- **Target domain.** The apex to assess. Subdomain enumeration expands it, so
  you name one domain rather than assembling the list of names to check, which is
  the list you would have got wrong anyway.
- **Check list.** The apex plus every discovered subdomain, deduped and capped
  before the sweep runs, so an over-enumerated domain does not turn into an
  unbounded set of lookups.
- **Credentials.** None. Every check is a DNS query, so there is no mail
  provider to authenticate against and no API token to supply.

## Outputs

- **Per-name posture.** The SPF, DKIM, DMARC, MTA-STS and BIMI state resolved
  for every name in the check list, not only the apex.
- **Spoofability verdict.** One per name, decided from the policy that actually
  applies to it after the DMARC organizational tree-walk rather than from the
  records published under that name.
- **Remediation notes.** What to publish, per domain, so the report doubles as
  the ticket.
- **PDF report.** The exposure list and the remediation in one file you can
  send to whoever owns DNS.

## Sample output

Illustrative, in the record shape the workflow emits. The run this was modelled
on was against a third party's live mail domain, so the names are replaced with
documentation domains.

Each name carries the effective policy a receiver would apply, not just the record
on the name itself:

```jsonl
{"domain": "example.com", "is_apex": true, "verdict": "protected", "effective_policy": "reject", "own_dmarc": true, "dmarc_policy": "reject", "spf": "softfail", "has_mx": true, "dkim": ["selector1", "k1", "k2"], "bimi": true, "reasons": []}
{"domain": "www.example.com", "is_apex": false, "verdict": "protected", "effective_policy": "reject", "own_dmarc": false, "dmarc_policy": null, "spf": "inherit", "has_mx": false, "dkim": [], "reasons": []}
{"domain": "mail.example.com", "is_apex": false, "verdict": "protected", "effective_policy": "reject", "own_dmarc": false, "dmarc_policy": null, "spf": "strict", "has_mx": true, "dkim": ["selector1"], "reasons": []}
{"domain": "access.example.com", "is_apex": false, "verdict": "spoofable", "risk": 4, "effective_policy": "none", "own_dmarc": true, "dmarc_policy": "none", "spf": "strict", "has_mx": true, "dkim": ["default"], "reasons": ["DMARC policy p=none (monitor only)"]}
{"domain": "status.example.com", "is_apex": false, "verdict": "protected", "effective_policy": "reject", "own_dmarc": false, "dmarc_policy": null, "spf": "inherit", "has_mx": false, "dkim": [], "reasons": []}
{"domain": "marketing.example.com", "is_apex": false, "verdict": "spoofable", "risk": 3, "effective_policy": "none", "own_dmarc": true, "dmarc_policy": "none", "spf": "softfail", "has_mx": true, "dkim": [], "reasons": ["DMARC policy p=none (monitor only)", "No DKIM selectors found"]}
```

[subfinder](/tools/subfinder) expands that apex to every name it can find. Most
of them publish no DMARC record of their own and inherit the apex `p=reject`,
which is what you want. The ones worth your attention publish a record that
enforces nothing, and so opt themselves back out of the protection the apex
bought.

The run summary is the fix list in one line:

```json
{"apex": "example.com", "apex_verdict": "protected", "apex_policy": "reject", "domains_checked": 200, "spoofable": 1, "mail_sending": 150, "protected": 199}
```

The tree-walk is what produced those verdicts. Sibling names with no record of
their own inherit `p=reject` from the apex and score as protected; the one above
overrides that inheritance with `p=none` and reopens itself. Reading each name's
records in isolation gets both cases wrong, which is why the sweep resolves
policy down the tree rather than per name.

## FAQ

### Does it send any email?

No. Every check is a DNS lookup, so nothing is delivered, no test message lands
in anybody's inbox, and the target's mail servers are never contacted.

### What is the DMARC organizational tree-walk?

It is how a receiving mail server decides which DMARC policy applies to a name.
If the name publishes no record of its own, the receiver walks up to the
organizational domain and applies its policy, or its subdomain policy where one
is set. Reading each name's own records in isolation therefore produces the wrong
answer twice over: it reports a protected subdomain as unprotected, and it misses
that a permissive subdomain policy at the apex reopens every name below it.

### Do you need access to the mail provider?

No. There are no credentials and no API token anywhere in the run, which is also
why the same check works against a domain you do not administer.

### Why check subdomains when mail comes from the apex?

Because a receiver does not care where you intended mail to come from. Any name
that resolves is a name somebody can try to send as, and the ones set up years
ago for a marketing tool or a status page are exactly the ones nobody revisited.

## Related workflows

- [Typosquat & Look-alike Domain Scanner](/library/typosquat-and-look-alike-domain-scanner).
  Reach for this instead when the phishing risk is a domain that merely resembles
  yours rather than mail forged from a name you own.
- [Domain OSINT & Footprinting](/library/domain-osint-and-footprinting). Reach
  for this instead when you want the full DNS and certificate picture, not only
  the mail-authentication slice of it.
- [Find Subdomain Takeovers](/library/subdomain-takeover-scanner). Reach for
  this instead when the concern is a subdomain an attacker can claim outright
  rather than mail they can forge.

---
_Markdown source of https://trickest.com/library/email-spoofing-scanner-dmarc-spf-dkim._
