Security Spot-Checks

How do you check SPF, DKIM, and DMARC for a domain?

You check SPF, DKIM, and DMARC by reading three DNS TXT records. SPF sits at the domain root, DMARC at _dmarc.yourdomain, and DKIM at selector._domainkey.yourdomain (you need the selector). Bassethound reports all three as email-auth booleans in the infrastructure layer of its five-layer dossier, in one call.

Best move: query three DNS TXT records, one per mechanism, and read the policy tags, not a green checkmark from a scanner.

Why it works: all three live in DNS TXT records, so the ground truth is a public lookup anyone can run, no login and no vendor tool.

Key takeaways

  • SPF lives in a TXT record at the domain root and starts with v=spf1. DMARC lives at _dmarc.<domain> and starts with v=DMARC1.
  • DKIM is the hard one. Its record sits at <selector>._domainkey.<domain>, and selectors are not discoverable from DNS. You need the selector from a signed message or a common guess.
  • Presence is not protection. A DMARC record with p=none monitors but enforces nothing. The p= tag is the field that matters.
  • SPF caps at ten DNS-querying mechanisms (RFC 7208). Cross that limit and evaluation returns permerror.
  • Bassethound returns SPF, DKIM, and DMARC as email-auth booleans in the infrastructure layer, correlated with the MX record, email provider, and TLS cert, in one call.

How do you check an SPF record?

SPF is one TXT record at the domain apex. Query it and look for a string that starts with v=spf1. The content is a list of mechanisms (ip4, ip6, a, mx, include) that authorize sending sources, closed by an all mechanism. -all is a hard fail (reject anything not listed), ~all is a soft fail (accept but mark), and +all is a mistake that authorizes the whole internet. One SPF record per domain is the rule. Two v=spf1 records are a misconfiguration that resolves to permerror. Then count the DNS-querying mechanisms: include, a, mx, ptr, exists, redirect. Ten is the ceiling per RFC 7208. Large providers nest includes (include:_spf.google.com fans out to several lookups), so an organization can blow the limit without noticing. dig +short TXT example.com returns the record. Match the v=spf1 prefix, then parse the mechanisms left to right until you hit the terminating all.

Why is DKIM harder to check than SPF or DMARC?

Because DKIM has no fixed location. SPF and DMARC each sit at one predictable name (the root, and _dmarc). DKIM records live at <selector>._domainkey.<domain>, and the selector is chosen by whoever configured signing. There is no DNS mechanism to list selectors. A domain can run several at once, rotate them, or use provider-specific names. So to confirm DKIM from DNS alone you either know the selector already or you guess. Common selectors: google (Workspace), selector1 and selector2 (Microsoft 365), k1 (Mailchimp), s1/s2, default, dkim. The reliable path is to read a real message the domain sent. The DKIM-Signature header carries s=<selector> and d=<domain>. Take that selector, query <selector>._domainkey.<domain> for TXT, and you get the public-key record (v=DKIM1; k=rsa; p=...). No message in hand means guessing, and a null result never proves absence. It proves only that your guessed selectors missed.

What does the DMARC policy tell you?

DMARC lives at _dmarc.<domain> as a TXT record starting v=DMARC1. The tag that matters is p=: none (monitor only), quarantine (route failures to spam), or reject (block them). A published record with p=none is common and misleading. It means the owner is watching but not enforcing, so spoofed mail still lands. Other tags carry weight too: rua= (aggregate report address), ruf= (forensic reports), pct= (the share of mail the policy applies to, so pct=50 with p=reject rejects only half), sp= (subdomain policy), and adkim/aspf (alignment strictness, s strict or r relaxed). DMARC works by alignment: a message passes when SPF or DKIM authenticates AND the authenticated domain aligns with the visible From. That is why DMARC needs SPF or DKIM underneath it. Read p= and pct= together for the real enforcement level, since a published record with p=none enforces nothing.

How do you check all three in one pass?

Three lookups, one pass. Query TXT at the root for SPF, TXT at _dmarc.<domain> for DMARC, and TXT at <selector>._domainkey.<domain> for each selector you know. Batch them: dig +short TXT example.com, dig +short TXT _dmarc.example.com, dig +short TXT google._domainkey.example.com. Parse by prefix: v=spf1, v=DMARC1, v=DKIM1. For an agent running this across many domains, the cost is not the DNS lookups (cheap and cacheable) but the correlation. Email auth means little in isolation. SPF plus DMARC p=reject plus a valid DKIM key plus a known email provider (from the MX record) is a coherent picture of a domain that takes deliverability seriously. Bassethound runs the three lookups as part of the infrastructure layer, next to MX, email provider, TXT vendors, and the TLS cert, so you read the auth posture beside the rest of the stack instead of stitching it together across tools.

What can a keyless, static check miss?

Be honest about three limits. First, DNS-only checks confirm what is published, not what receivers enforce. You can read p=reject and still not know a given mailbox provider honors it. Second, DKIM presence is only as good as your selector list. A domain can sign with a selector you did not test, so a “no DKIM” result from guessing is a maybe, not a no. Reading an actual signed message is the only sure path. Third, SPF include chains resolve at send time. A static snapshot does not expand every nested include or catch a chain that only crosses ten lookups under certain conditions. Bassethound reports SPF, DKIM, and DMARC as booleans and flags when a missing selector blocked DKIM confirmation, rather than dressing a guess as a clean negative. The dossier states the gap instead of hiding it behind a checkmark.

Bassethound perspective

Most email-auth checkers sell you a color. Green for SPF, green for DMARC, a checkmark for DKIM. They lie twice. They treat p=none as a pass when it enforces nothing, and they claim DKIM with a checkmark after testing one common selector. A domain can fail your guess and sign every message it sends. We report DKIM as a boolean and tell you when a selector blocked confirmation, because a false negative worn as fact is worse than an honest gap.

Email auth is not a standalone report. Whether a domain enforces DMARC correlates with the rest of the infrastructure layer: its MX provider, its TXT vendors, its cert hygiene. A dedicated email-auth tool hands you three records and stops. Bassethound puts those three next to the MX record, the email provider, the TLS cert, and the tech stack, in one call. Read the auth posture as a lead, not a verdict. An agent stitching five single-purpose MCPs together pays for every seam. One call hands you the correlation instead.

Sources

Frequently asked questions

Can you find a DKIM record without knowing the selector?

Not from DNS alone. DKIM records live at selector._domainkey, and selectors are not enumerable. You read the selector from a signed message's DKIM-Signature header (the s= tag) or test common ones (google, selector1, default, k1).

What counts as a passing DMARC policy?

p=quarantine or p=reject tells receivers to act on failures. p=none only monitors and enforces nothing. A record present with p=none is published but not protecting you.

Does an SPF record alone stop spoofing?

No. SPF authorizes sending IPs for the envelope-from, but the visible From header can still be forged. DMARC ties SPF and DKIM to the From domain and tells receivers what to do on failure.

How many DNS lookups can an SPF record use?

Ten. RFC 7208 caps SPF at ten DNS-querying mechanisms (include, a, mx, ptr, exists, redirect). Exceed it and evaluation returns permerror, which many receivers treat as a fail.

Does Bassethound return the full DMARC policy or just presence?

The infrastructure layer returns email-auth booleans (SPF, DKIM, DMARC present or not). For the exact policy string, read the _dmarc TXT record directly. Bassethound flags the gap when a missing selector blocks DKIM confirmation.

Sniff a domain.

Run sniff_domain on any site and read its five-layer dossier in one call.

Sniff a domain