DevKitLab Logo DevKitLab
邮件 / 邮件头 / DMARC / 安全 / 钓鱼邮件

怎么读邮件头(顺便认出伪造的发件人)

邮件头是一封信如何抵达的唯一第一手记录。读懂 Authentication-Results、倒着走 Received 链,认出伪造发件人留下的那几处破绽。

有人转来一封邮件,问那个没法一句话回答的问题:「这是真的吗?」

正文帮不上忙。logo 几秒钟就能扒下来,语气可以模仿,而链接的显示文字从来就不必和它真正指向的地方一致。但每封邮件还带着另一份文件,一份发信方无法完全控制的文件——邮件头。那一段记录了哪些服务器经手过这封信、什么时候经手的,以及收信方对「这是谁发的」得出了什么结论。这是电子邮件里最接近保管链的东西。

这篇讲怎么读它。如果你想先补上那几个缩写背后的模型——SPF、DKIM、DMARC 各验什么、对齐为什么关键——SPF、DKIM、DMARC 到底各管什么讲的是那个。这里默认你已经有了,直接开始读一封真实的信。

先拿到原始邮件头

你要的是源码,不是客户端显示出来的样子:

  • Gmail——打开邮件,右上角三点菜单,显示原始邮件
  • Outlook(桌面版)——文件 → 属性,读「Internet headers」那个框。
  • Apple Mail——显示 → 邮件 → 所有标头,或者 ⌥⌘U 看原始源码。
  • Thunderbird——Ctrl+U

从最上面一直复制到第一个空行为止。那个空行就是头部结束、正文开始的地方。

在把这段东西粘到任何地方之前,有个习惯值得先养成:邮件头里含有收件人地址、内网主机名,往往还有私有 IP 段。所以「交给哪个工具」是件需要挑的事。邮件头分析工具完全在你浏览器里解析,什么都不上传,而且可以在你复制出去的内容里把邮箱名遮掉,这样一段邮件头才好拿去和同事讨论。

从结论读起:Authentication-Results

别从文件最上面开始读。先看那一行——有服务器把自己的结论写在了那儿:

Authentication-Results: mx.example.net;
  spf=pass smtp.mailfrom=bounces.provider.com;
  dkim=pass header.d=yourcompany.com;
  dmarc=pass header.from=yourcompany.com

把它读成三条独立结论,外加每条各自作用于哪个身份:

  • spf=pass smtp.mailfrom=...——连接过来的 IP 被授权替那个域名发信。注意看那个域名:它经常是服务商的退信域名,而不是 From 里的那个。
  • dkim=pass header.d=...——有一个签名验过了,而且是那个域名的密钥签的。
  • dmarc=pass header.from=...——SPF 或 DKIM 的通过,属于一个与可见 From 对齐的域名。三条里只有这一条说的是读者看到的那个发件人。

关于这个头有两点提醒,都很重要。

先看最上面的普通记录,但不能只因为它在最上面就相信它。 它通常是最新的结果,而链路上游任何人都可以插一行写着 dmarc=pass 的头。开头那个 authserv-id(上例里的 mx.example.net)指明是谁在下这个结论;只有当它属于一个你确实信任的邮件系统时才有意义——通常是你自己的服务商。来自陌生 authserv-id 的 dmarc=pass,仍然只是陌生人的说法。

它是记录,不是复核。 收信方在投递那一刻做了这些判断并把结果写下来。你现在做什么都不会让它们重跑一遍,而 DNS 可能早就变了。

如果这个头压根不存在,那也证明不了什么——很多系统就是不加。它只说明这条捷径没有,你得把剩下的读得更仔细些。

spf=passdmarc=fail 并排出现

这个组合最容易让人以为撞见了 bug,也是最值得一眼认出来的东西:

Authentication-Results: mx.example.net;
  spf=pass smtp.mailfrom=bounces.mailer.example;
  dkim=none;
  dmarc=fail header.from=yourcompany.com

什么都没坏。SPF 确实通过了——是替 bounces.mailer.example 通过的,而这个域名跟 yourcompany.com 没有任何关系。又没有 DKIM 签名来补上另外半边。于是没有任何一个经过认证的身份与 From 头对齐,DMARC 判失败。

这个形态有两种截然不同的解释,把它们分开才是真正要做的事:

  • 一个从没配过对齐的合法发信方——某个营销平台或工单系统以你的名义发信,却没有在你的域名下做 DKIM 签名。极其常见,在服务商那边就能修。
  • 彻头彻尾的伪造——别人的域名通过了 SPF,而 From 里放的是你的名字。

剩下的邮件头,就是用来区分这两者的。

倒着走 Received 链

Received: 是路由历史。关键机制是:每台服务器都把自己那行加在最前面。所以这条链是最新的在上——最顶上那条是最后一跳,最下面那条自称是起点。

Received: from mx.example.net by inbox.example.net; Tue, 4 Aug 2026 10:00:12 +0000
Received: from mail.sender.example (mail.sender.example [198.51.100.7])
        by mx.example.net; Tue, 4 Aug 2026 10:00:09 +0000

从下往上读,就是顺着时间跟着这封信走。有三样东西值得提取出来:

它从哪儿进入你的基础设施。 找到最下面那条由你信任的服务器写的记录,那就是边界——这条线很重要,因为它下面的每一行都是别人写的。一封伪造的邮件可以在那条线以下携带一整段编造的历史,主机名和时间戳都编得很像。边界以下,把每一跳都当成「说法」而不是「事实」。

方括号里的 IP。 取方括号里的字面地址([198.51.100.7]),而不是它前面那个主机名:主机名是连接方自称的,方括号里的地址才是你的服务器实际观察到的。如果来源 IP 和发信方的身份对不上——某银行的邮件从一个不相干国家的住宅宽带段发出来——这就值得追下去。丢进 IP 地址查询看看它到底归谁。

跳与跳之间的时间。 每一跳都带时间戳。差几分钟通常只是排队。更值得注意的是对不上的间隔——一封信在某处躺了几个小时,或者各跳时间戳是倒着走的(这多半意味着某台机器时钟不准,偶尔意味着这条链是手工拼出来的)。

身份字段,以及它们互相打架的地方

一封邮件会在好几个地方声明「我是谁发的」,而伪造往往就表现为这些地方互相矛盾。

From:——读者看到的那个。 注意显示名是自由文本,跟真实地址没有任何关系:

From: "Support <support@yourbank.example>" <ceo@random-domain.example>

多数客户端只显示 Support <support@yourbank.example>,而真正的地址是后面那个。显示名里嵌着一个邮箱地址,可能是刻意误导。 操作前要核对真正的 From 地址;把它放回上下文看,它是很强的警示,但单独一项还不构成证据。

Return-Path:——退信去哪儿。 这是投递时记录的信封发件人。它和 From 域名不一致是家常便饭且完全合法——那通常只是服务商在替你处理退信。它可以提供背景,但不能证明 SPF 实际评估的是谁:SPF 可能评估 SMTP MAIL FROM,信封发件人为空时也可能评估 HELO。有的话,请看 Authentication-Results 里的 smtp.mailfromsmtp.helo,那才是接收方记录下来的身份。

Reply-To:——你的回复去哪儿。 在不少配置里同样合法(共享收件箱、客服系统)。但在一封本来就 DMARC 失败的邮件里,一个指向不相干域名的 Reply-To,就是把整段对话导向攻击者的那个机关。

Message-ID:——分配的标识符。 它的域名部分通常是生成它的系统。服务商代发时与 From 域名不同属于正常。格式错乱或者干脆没有,值得记一笔。

单看任何一项都定不了罪。这正是重点:信号在于模式,而不在于某一个字段。一个 DMARC 失败的 From 域名,加上一个藏着别的地址的显示名,再加上一个指向别处的 Reply-To——这三样凑成一个自洽的故事,而单纯的配置错误讲不出这个故事。

读一个 DKIM 签名

如果有 DKIM-Signature: 头,几个标签决定了它值多少:

DKIM-Signature: v=1; a=rsa-sha256; d=yourcompany.com; s=selector1;
  h=from:to:subject:date; bh=...; b=...
  • d=——签名域名。拿它和 From 域名比。一个来自不相干域名的、完全合法的签名,对 DMARC 毫无贡献。
  • s=——selector,它和 d= 一起定位到 selector1._domainkey.yourcompany.com 上的公钥。你可以用 DNS 查询工具自己把那条 TXT 取出来,确认那儿确实发布了密钥。
  • h=——被签名覆盖的头部列表。一个不含 from 的签名基本等于没有:可见发件人可以在不惊动它的情况下被换掉。这一项值得专门看,因为它是一种很隐蔽的失效——出问题了它照样报 dkim=pass
  • l=——正文长度上限,只签前 N 个字节。超出部分追加的内容不在签名内。不常见,一旦出现就值得警惕。

有一条限制必须说清楚:读签名不等于验签。验签需要未经改动的正文和 DNS 里的公钥,而且传输途中任何改动都会让一条本来完好的签名失效。分析工具读的是这些字段、检查上面那些结构性质;Authentication-Results 里的 dkim=pass 才是收信方在投递时做的验签。

ARC 那几个头,以及它们值多少

转发和邮件列表天生就会打断认证:中继用的是它自己的 IP,SPF 于是失败;列表动过邮件,DKIM 也跟着失效。ARC(认证接收链,RFC 8617)存在的意义,就是把断裂之前的那个判决带过来。读 Google 或微软经手的真实邮件,你会不停看到它:

ARC-Seal: i=1; a=rsa-sha256; cv=none; d=lists.example.org; s=arc; b=...
ARC-Message-Signature: i=1; a=rsa-sha256; d=lists.example.org; s=arc;
  h=from:to:subject; b=...
ARC-Authentication-Results: i=1; lists.example.org;
  spf=pass smtp.mailfrom=you@yourcompany.com;
  dkim=pass header.d=yourcompany.com; dmarc=pass

每经过一个中间方就是三个头,用 i= 编号:

  • ARC-Authentication-Results——那一跳当时看到的认证结果,是它动手改动之前的样子。这是真正有内容的那个。
  • ARC-Message-Signature——那一跳对邮件本身的签名,让它留下的这份快照事后改不了。
  • ARC-Seal——对本身的签名,正是它让这些记录成为一条链,而不是一堆各说各话的声明。

i= 读作位置:i=1 是第一个中间方,i=2 是在它之后重新封签的。在编号最大的那条 ARC-Seal 上,cv= 报告的是链校验结果——第一跳是 none,此前的链验过了是 pass,没验过是 fail。看到 cv=fail 就意味着链在某处断了,它携带的那些结果什么也证明不了。

接下来是判断一封信时真正要紧的那点:ARC 不是绕过机制,链有效也不等于 DMARC 通过。 任何人都能挂一组 ARC 上去,声称这封信什么都通过了——攻击者也能。它只有在最终收信方信任那个封签域名时才算证据,而这跟你前面对 authserv-id 用的是同一套判断。收信方自己决定认哪些中间方;ARC 给的是做这个决定的材料,不是必须认的义务。

所以把 ARC 链当解释读,别当判决读:它告诉你一封转发过来的信为什么 SPF 和 DKIM 都没过却照样投递到了。分析工具会报出找到几组 ARC 以及链校验状态,让你一眼看出这封信到底有没有经过中间方。

「伪造」和「合法但配错」并排看

两者都 DMARC 失败。区分靠的是邮件头:

信号配错了但合法大概率是伪造
来源 IP某个已知服务商的地址段不相干的主机商、住宅宽带,或国别对不上
SPF 域名服务商的退信域名一个不相干的一次性域名
DKIM没有,或 d= 是服务商的没有,或由不相干域名签的
显示名平平无奇里面嵌着邮箱地址,或在模仿另一个发件人
Reply-To没有,或同一组织不相干的域名
Received 链连贯,主机名合理有断裂、时间戳异常、边界以下有编造的跳
整体模式只有一处不对好几处指向同一个方向

诚实的总结是:孤立的一处异常通常意味着配置有误;好几处指向同一个方向,就该把这封信当成敌意的来处理。

一份阅读清单

  1. 拿到原始源码,复制到第一个空行为止。
  2. 先读 Authentication-Results——并确认 authserv-id 属于你信任的系统。
  3. 记下每条结论作用于哪个域名,而不只是通过还是失败。陌生域名上的 spf=pass 对 From 那一行什么都证明不了。
  4. 在 Received 链里找到你的信任边界,它以下的都是说法。
  5. 取入口那一跳方括号里的 IP,查清它归谁。
  6. 比对身份字段——From 地址与显示名、Return-Path、Reply-To、Message-ID。
  7. 检查 DKIM 签名的 d=h=,确认 from 被覆盖。
  8. 如果有 ARC,看编号最大那条的 cv=——并且问一句:封签的那个域名你信得过吗。
  9. 权衡整体模式,而不是某一个字段。

与其每次都靠肉眼这么过一遍,不如把这段头粘进邮件头分析工具:它会把逐跳路径连同每跳耗时铺开,把认证结果真正解析出来而不只是原样回显,标出显示名欺骗和身份错配,并把 DKIM 签名逐个字段拆开。全程本地运行,邮件不离开你的机器。

而如果结论是你自己的域名正在被伪造,或者你某个合法发信方过不了对齐,那就不是取证问题而是配置问题了。用 SPF/DMARC 检测工具看看你的域名公布了什么;如果答案是强制还没开,从 p=none 走到 p=reject 讲的就是怎么分阶段把它开起来而不在路上弄丢邮件。