跳到主要内容
ecdsa.com

DKIM vs SPF vs DMARC:各自检查什么,为什么三者缺一不可

SPF 为发送服务器担保,DKIM 对邮件签名,DMARC 把两者与收件人看到的 From 地址关联起来,并告诉接收方在检查未通过时如何处理。

DKIM、SPF 和 DMARC 并不是择一而用的替代方案:三项标准回答的是不同的问题,只有组合在一起才能发挥作用。SPF 列出哪些服务器可以代表信封发件人域名发送邮件;DKIM 为邮件附加签名,签名所用的密钥发布在某个域名下;DMARC 要求两者中至少有一项针对与可见的 From: 地址相匹配的域名通过,并公布域名所有者的策略和报告接收地址。

概览

SPFDKIMDMARC
规范RFC 7208(2014年)RFC 6376(2011年)、RFC 8301、RFC 8463RFC 9989、9990、9991(2026年5月),取代 RFC 7489
回答的问题这个 IP 能否代表信封发件人域名发送邮件?这份内容是否由该域名签名,并且未被改动?是否有通过身份验证的域名与 From: 域名匹配?不匹配时怎么办?
检查的身份MAIL FROM(Return-Path)和 HELODKIM-Signature 中的 d= 域名From: 头部中的域名
发布位置该域名上的 TXTselector._domainkey.domain 处的 TXT_dmarc.domain 处的 TXT
经普通转发后是否仍有效否(转发方的 IP 不在列表中)是,前提是邮件未被修改是,依靠 DKIM
经过会修改邮件的邮件列表后是否仍有效否否(正文或已签名的头部发生变化)仅当邮件列表改写 From: 或接收方信任 ARC 时
硬性限制每次评估最多 10 个会触发 DNS 查询的项,空查询最多 2 次RSA 至少 1024 位(推荐 2048 位);禁止 rsa-sha1DNS 树遍历最多 8 次查询
单独使用时的作用只给出结果,由接收方决定只给出结果,由接收方决定域名所有者的策略:none、quarantine、reject
报告无无XML 汇总报告(rua)、失败报告(ruf)

三者的工作原理

SPF 在 SMTP 会话期间评估:接收方取信封发件人的域名(退信则取 HELO 中的域名),获取该域名的 TXT 记录,再把建立连接的 IP 与记录中列出的 ip4、ip6、a、mx 和 include 项逐一比对。结果为 pass、fail(-all)、softfail(~all)、neutral、none 或错误。每个 include、a、mx、ptr、exists 和 redirect 都要消耗一次 DNS 查询,超过 10 次结果就会变成 permerror(RFC 7208 §4.6.4),该域名所有邮件的 SPF 检查都会因此失效。

DKIM 对规范化后的正文(bh=)和列出的头部字段(h=)计算哈希,并用该域名的私钥签名。接收方从以 s= 选择器和 d= 域名命名的 TXT 记录(例如 google._domainkey.github.com)中获取公钥,然后验证签名。RFC 8301 禁止使用 rsa-sha1,并要求密钥至少为 1024 位;RFC 8463 新增了 ed25519-sha256,由于验证方的支持还不完整,它通常与 RSA 签名一并提供。

DMARC 在两者之后运行:如果 SPF 针对与 From: 对齐的域名通过,或者某个 DKIM 签名针对与 From: 对齐的域名验证成功,DMARC 即为通过。对齐默认采用宽松模式(组织域相同即可,因此 mail.example.com 与 example.com 对齐),设置 aspf=s 和 adkim=s 则为严格模式。

下面的脚本读取的是真实记录(2026年10月20日,dig 9.10.6 和 OpenSSL 3.6.5)。

用 dig 读取 SPF、DMARC 和 DKIM 记录
echo "== SPF: google.com and its include"
dig +short TXT google.com | grep "v=spf1"
dig +short TXT _spf.google.com
echo "== DMARC: google.com and github.com"
dig +short TXT _dmarc.google.com
dig +short TXT _dmarc.github.com
echo "== DKIM: key size behind two github.com selectors"
for sel in google pf2023; do
  printf '%s: ' "$sel"
  dig +short TXT "$sel._domainkey.github.com" | tr -d '" \n' | sed 's/.*p=//' \
    | openssl base64 -d -A | openssl pkey -pubin -inform DER -noout -text | head -1
done
输出
== SPF: google.com and its include
"v=spf1 include:_spf.google.com ~all"
"v=spf1 ip4:74.125.0.0/16 ip4:209.85.128.0/17 ip6:2001:4860:4864::/56 ip6:2404:6800:4864::/56 ip6:2607:f8b0:4864::/56 ip6:2800:3f0:4864::/56 ip6:2a00:1450:4864::/56 ip6:2c0f:fb50:4864::/56 ~all"
== DMARC: google.com and github.com
"v=DMARC1; p=reject; rua=mailto:mailauth-reports@google.com"
"v=DMARC1; p=quarantine; sp=reject; pct=100; rua=mailto:dmarc@github.com; ruf=mailto:dmarc@github.com; fo=1"
== DKIM: key size behind two github.com selectors
google: Public-Key: (2048 bit)
pf2023: Public-Key: (1024 bit)

有三个细节值得注意。Google 的 SPF 以 ~all(softfail)结尾,而它的 DMARC 策略是 reject:DMARC 处于强制执行状态时,起决定作用的是 DMARC 结果,因此 SPF 限定符影响不大。GitHub 仍在发布 pct=100,而 RFC 9989 已经移除了这个标签;接收方会忽略未知标签,而且 100 本来就是默认值,所以并无害处。另外,GitHub 的一个选择器仍在使用 1024 位密钥,这是 RFC 8301 允许的最小值。电子邮件身份验证分析器可以对任意域名执行同样的检查。

对齐决定结果

下表列出一封带有 From: news@example.com 的邮件在几种典型发送场景下的结果。

场景SPFDKIMDMARC
你自己的服务器,以 example.com 签名pass,已对齐pass,已对齐pass
使用自有退信域名的邮件服务提供商,以 example.com 签名针对服务商的域名 pass,未对齐pass,已对齐pass(DKIM)
只用自己的域名签名的服务商pass,未对齐pass,未对齐fail
被邮箱规则原样转发fail(转发方的 IP)pass,已对齐pass(DKIM)
邮件列表添加页脚或主题标签fail 或未对齐fail(正文哈希)fail,除非 From: 被改写或 ARC 受信任
从他人服务器发出、伪造的 From:fail 或 nonenonefail,策略生效

第三行是最常见的部署错误:发送方通过了 SPF 和 DKIM,却没有通过 DMARC,因为两个通过身份验证的域名都不属于你。让每个服务商都用你域名下的密钥签名(通常是一条指向服务商密钥的 CNAME),如果还希望 SPF 也对齐,就使用自定义退信域名。

转发、邮件列表与 ARC

SPF 验证的是最后一跳,所以任何转发都会让它失效;发件人重写方案能让 SPF 针对转发方自己的域名通过,但永远无法让它对齐。DKIM 验证的是内容,因此能经受不改动邮件的中继,而邮件列表添加页脚或标签时就会失败。ARC(RFC 8617,实验性)允许中转方为自己看到的身份验证结果加封印,信任该封印方的接收方即使遇到 DMARC 未通过,也可以接受这封邮件;ARC 链查看器可以显示这条链。剩下的弱点是 DKIM 重放:签名有效的邮件可以被重新发送给新的收件人,IETF 的一个工作组正在其 DKIM2 草案中解决这个问题。

邮箱服务商的要求

自2024年2月1日起,Gmail 要求所有发件人使用 SPF 或 DKIM,配置有效的正向和反向 DNS,使用 TLS,并且被举报的垃圾邮件率低于 0.3%。每天向个人 Gmail 账号发送 5,000 封及以上邮件的发件人,还需要同时配置 SPF 和 DKIM,发布 DMARC 记录(p=none 即可),让 From: 与 SPF 或 DKIM 对齐,并为营销邮件提供一键退订(RFC 8058)。Yahoo 在同一时间推出了相应的规则。Microsoft 自2025年5月5日起对 Outlook.com 采用相同的门槛,并以 550 5.7.515 拒收不合规的邮件。BIMI 徽标还要求 DMARC 策略为 quarantine 或 reject。

RFC 9989(2026年5月)把 DMARC 纳入标准轨道,并修改了记录格式:pct、rf 和 ri 被移除,测试时用 t=y 取代 pct=0,np 为不存在的子域名设置策略,psd 标记公共后缀域名,查找组织域时用 DNS 树遍历取代公共后缀列表(Public Suffix List)。

三者各自的用途

  • SPF:授权你自己的外发服务器,以及使用你的域名作为退信地址的服务商;在从不发送邮件的域名和子域名上发布 v=spf1 -all。
  • DKIM:用你自己的域名为所有外发邮件签名,包括通过服务商发送的邮件;至少每年轮换一次选择器,并把 1024 位密钥替换为 2048 位密钥。
  • DMARC:先收集报告,再强制执行;添加 np=reject(或 sp=reject),让攻击者无法利用编造的子域名。
  • 停放域名:v=spf1 -all、v=DMARC1; p=reject;,且不设 DKIM 选择器。
  • 传输保护和内容保护属于另外的问题:服务器之间的 TLS 用 MTA-STS,端到端签名用 S/MIME 或 OpenPGP。

迁移说明

  1. 盘点发送方:在 p=none 下用 DMARC 汇总报告观察两到四周;每个合法来源都必须显示 DKIM 已对齐、SPF 已对齐,或两者都已对齐。
  2. 先修复 DKIM:在每个服务商处启用自定义域名签名,发布 2048 位密钥(DKIM 记录生成器会把密钥拆分成 255 个字符的字符串),并用 DKIM 调试器验证。
  3. 精简 SPF:删除不再使用的 include,把查询次数降到 10 次以下;只有在能够自动保持 IP 段更新的情况下,才把记录扁平化为 IP 段。
  4. 逐步强制执行:先用带 t=y 的 p=quarantine 观察,然后切换到 p=quarantine,再到 p=reject;始终保持接收 rua 报告。
  5. 保护子域名:使用 sp 或 np,并在每个不应发送邮件的域名上发布空记录。

参考资料

全部对比