DKIM、SPF 和 DMARC 并不是择一而用的替代方案:三项标准回答的是不同的问题,只有组合在一起才能发挥作用。SPF 列出哪些服务器可以代表信封发件人域名发送邮件;DKIM 为邮件附加签名,签名所用的密钥发布在某个域名下;DMARC 要求两者中至少有一项针对与可见的 From: 地址相匹配的域名通过,并公布域名所有者的策略和报告接收地址。
概览
| SPF | DKIM | DMARC | |
|---|---|---|---|
| 规范 | RFC 7208(2014年) | RFC 6376(2011年)、RFC 8301、RFC 8463 | RFC 9989、9990、9991(2026年5月),取代 RFC 7489 |
| 回答的问题 | 这个 IP 能否代表信封发件人域名发送邮件? | 这份内容是否由该域名签名,并且未被改动? | 是否有通过身份验证的域名与 From: 域名匹配?不匹配时怎么办? |
| 检查的身份 | MAIL FROM(Return-Path)和 HELO | DKIM-Signature 中的 d= 域名 | From: 头部中的域名 |
| 发布位置 | 该域名上的 TXT | selector._domainkey.domain 处的 TXT | _dmarc.domain 处的 TXT |
| 经普通转发后是否仍有效 | 否(转发方的 IP 不在列表中) | 是,前提是邮件未被修改 | 是,依靠 DKIM |
| 经过会修改邮件的邮件列表后是否仍有效 | 否 | 否(正文或已签名的头部发生变化) | 仅当邮件列表改写 From: 或接收方信任 ARC 时 |
| 硬性限制 | 每次评估最多 10 个会触发 DNS 查询的项,空查询最多 2 次 | RSA 至少 1024 位(推荐 2048 位);禁止 rsa-sha1 | DNS 树遍历最多 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)。
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 的邮件在几种典型发送场景下的结果。
| 场景 | SPF | DKIM | DMARC |
|---|---|---|---|
你自己的服务器,以 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 或 none | none | fail,策略生效 |
第三行是最常见的部署错误:发送方通过了 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。
迁移说明
- 盘点发送方:在
p=none下用 DMARC 汇总报告观察两到四周;每个合法来源都必须显示 DKIM 已对齐、SPF 已对齐,或两者都已对齐。 - 先修复 DKIM:在每个服务商处启用自定义域名签名,发布 2048 位密钥(DKIM 记录生成器会把密钥拆分成 255 个字符的字符串),并用 DKIM 调试器验证。
- 精简 SPF:删除不再使用的 include,把查询次数降到 10 次以下;只有在能够自动保持 IP 段更新的情况下,才把记录扁平化为 IP 段。
- 逐步强制执行:先用带
t=y的p=quarantine观察,然后切换到p=quarantine,再到p=reject;始终保持接收rua报告。 - 保护子域名:使用
sp或np,并在每个不应发送邮件的域名上发布空记录。