← 返回文章目录

SPF、DKIM、DMARC 到底各管什么:从一封邮件的身份链路讲清楚

邮件里同时存在信封发件人、可见发件人、发送服务器和签名域。沿着一封邮件的投递过程,讲清 SPF、DKIM、DMARC 各自验证什么,以及为什么三者缺一不可。

SPFDKIMDMARC邮件认证
展开本文目录
  1. SPF 管的是 SMTP 信封
  2. DKIM 给邮件盖一枚可验证的域签名
  3. DMARC 把认证结果拉回用户看到的 From
  4. 上线时不要直接跳到 reject
  5. 参考资料

邮件认证最容易让人糊涂的地方,是一封邮件里其实有好几个“发件人”。

你在邮箱界面看到的是 From: billing@example.com。SMTP 传输时还有一个信封发件人,也就是 MAIL FROM,它可能是 bounce@mailer.example.com。真正连接收件服务器的,又是一台有具体 IP 的邮件服务器。邮件头里还可能带着 d=example.com 的 DKIM 签名。

这些身份可以相同,也可以不同。SPF、DKIM 和 DMARC 没有重复做同一件事,它们各自盯着链路中的一层:

  • SPF 检查“这台服务器有没有资格替某个信封域发信”。
  • DKIM 检查“邮件是否由某个域签过名,签名以后相关内容有没有被改”。
  • DMARC 检查“前两项验证出来的域,是否和用户眼里看到的 From 域对得上”,并告诉收件方验证失败时怎么处理。

把这条身份链理顺,很多看似矛盾的结果就不奇怪了。例如,SPF 显示 pass,DMARC 仍然可能失败;DKIM 签名有效,也不能直接证明可见发件人是真的。

SPF 管的是 SMTP 信封

假设 mailer.example.com 发布了下面这条 SPF 记录:

v=spf1 ip4:192.0.2.10 include:_spf.example.net -all

它表达的意思很直接:192.0.2.10include 引入的服务可以使用这个域作为 SMTP 的 MAIL FROM 身份,其他来源不被授权;当反向路径为空时,收件方会改用 HELO 身份评估 SPF。

收件服务器拿到一封邮件后,会把连接过来的客户端 IP 和信封域的 SPF 记录放在一起计算。匹配到授权来源,结果是 pass;命中 -all,则是 fail。这里验证的是 SMTP 会话里的身份,并不是用户在邮件客户端里看到的 From

这也是 SPF 最常见的误解。攻击者完全可以让信封发件人使用自己的域,取得 SPF pass,同时把可见 From 写成另一个品牌的地址。只看 SPF,无法回答“用户看到的发件人是否可信”。

转发邮件也会暴露 SPF 的边界。原始邮件经过转发服务器后,下一站看到的是转发服务器的 IP。如果转发方保留原来的 MAIL FROM,这个新 IP 往往不在原域的 SPF 授权名单里,SPF 就可能失败。有些系统会用 SRS 改写信封地址来处理这个问题,但那又会带来域名对齐的新问题。

SPF 记录本身也不能无限堆 includeamx。RFC 7208 对会触发 DNS 查询的机制设有处理上限。供应商一多,递归引用很容易超限,最后得到的不是安全性更强,而是 permerror。维护 SPF 的重点因此不在“把所有东西都写进去”,而在持续清点谁还在代表这个域发信。

DKIM 给邮件盖一枚可验证的域签名

DKIM 的思路和 IP 授权不同。发件系统选择部分邮件头和邮件正文,经过规范化与哈希计算,再用私钥生成签名,写入 DKIM-Signature 邮件头。一个简化后的签名会包含这些字段:

DKIM-Signature: v=1; a=rsa-sha256;
 d=example.com; s=selector1;
 h=from:to:subject:date;
 bh=...; b=...

d= 是承担签名责任的域,s= 是 selector。收件方把两者拼成类似 selector1._domainkey.example.com 的 DNS 名称,取回公钥,然后校验签名和正文哈希。

校验通过能说明两件事:控制 example.com 私钥的一方签过这封邮件;被签入的头部和正文,自签名以后没有发生会破坏签名的改动。它并不加密邮件,也不保证发件人是某个具体的人,更不代表邮件内容安全无害。

DKIM 比 SPF 更能经受普通转发,因为验证依靠签名,不依赖最后一跳的发送 IP。但邮件列表添加页脚、网关重写主题、系统修改 MIME 内容,都可能改变签入的数据,使原本有效的 DKIM 变成失败。relaxed 规范化可以容忍一部分空白和格式变化,无法容忍内容真的被改写。

实际运维时还要把 selector 当成密钥版本来管理。新公钥先发布到 DNS,发送端切换到新私钥,等待旧邮件离开传输链路后再撤掉旧公钥。直接覆盖旧密钥,可能让仍在队列里的邮件到达时无法验证。

DMARC 把认证结果拉回用户看到的 From

SPF 验证信封域,DKIM 验证签名域。DMARC 关心的是 RFC 5322 From,也就是邮件客户端通常展示给用户的那个地址。

它引入了一个关键概念:域对齐。

假设可见地址是 billing@example.com。这封邮件满足下面任意一条,DMARC 就可以通过:

  1. SPF 验证通过,而且 SPF 认证的 MAIL FROM 域与 example.com 对齐。
  2. DKIM 验证通过,而且签名里的 d= 域与 example.com 对齐。

默认的宽松对齐允许子域和组织域对应,例如 mailer.example.comexample.com 可以对齐;严格模式要求完整域名精确一致。注意,DMARC 要的是“验证通过并且对齐”。一个由 other.example.net 签出的合法 DKIM 签名,对 From: billing@example.com 没有帮助。

可以拿一封邮件做交叉验证。可见 From 是 billing@example.comMAIL FROM 使用 bounce@mailer.example.com,DKIM 却由第三方域 sender.example.net 签名。假如 SPF 通过,且采用默认的宽松对齐,mailer.example.comexample.com 具有相同的组织域,DMARC 仍然可以通过。反过来,如果信封域和签名域都属于第三方,即使 SPF、DKIM 各自都是 pass,它们没有一个与可见 From 对齐,DMARC 结果依旧是 fail

域所有者通过 _dmarc.example.com 发布策略:

v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com

p=none 是监控模式,不要求收件方因为 DMARC 策略改变投递动作;p=quarantine 建议把失败邮件当作可疑邮件处理;p=reject 建议拒收。rua 用来接收聚合报告,报告会告诉你哪些 IP 在使用这个 From 域、SPF 和 DKIM 的结果如何、是否对齐。2026 年发布的 RFC 9989 已取代早期的 RFC 7489,聚合报告格式则单独由 RFC 9990 规定。

DMARC 不能把邮件直接送进收件箱。收件方仍会综合 IP 和域名信誉、投诉、内容、发送节奏等信号。它解决的是域名冒用与身份对齐,不是完整的反垃圾系统。它也挡不住长得很像的相似域名,或把品牌名塞进显示名称的欺骗。收件地址栏看起来像某个品牌,不等于使用的就是那个品牌真正控制的域。

上线时不要直接跳到 reject

一个域往往同时被办公邮箱、业务系统、营销平台、工单系统和第三方服务使用。直接发布 p=reject,最先被拦下来的可能是自己遗漏的合法邮件。

更稳妥的做法是先盘点所有发信来源,给每条链路配置正确的 SPF 或 DKIM,并确保至少一条能和可见 From 对齐。然后用 p=none 收一段时间聚合报告,把未知来源分成三类:漏配的合法服务、早已停用的旧系统、真正的仿冒流量。确认主要链路都稳定后,再逐步提高到 quarantinereject

配置改完后,还要用真实邮件头里的 Authentication-Results 复核,DNS 查询成功不等于整条投递链已经对齐。

排障时也按身份链来看,不要只盯一个 pass

连接 IP
  -> SPF 检查 MAIL FROM/HELO
  -> DKIM 检查 d= 签名域和内容完整性
  -> DMARC 比较以上结果与可见 From 域
  -> 收件方结合本地信誉和内容策略决定投递

一封邮件能否通过 DMARC,问题通常不在某条 DNS 记录单独写得对不对,而在几套身份是否在同一条发送链路里闭合。SPF 给服务器发信资格,DKIM 给域签名能力,DMARC 才把这两份证明交到用户看到的发件人名下。

参考资料