email

Email 工具

邮件认证真正是怎么运作的:两个 From 地址的区别、SPF 与 DKIM 与 DMARC 各自证明什么、Received 链该怎么读,以及处理邮件头或 .eml 时该用哪个工具。

3 工具

§01 领域指南

两个 From 地址

几乎所有邮件冒用问题,追到底都是把两个发件人身份搞混了。信封发件人是 SMTP MAIL FROM 命令里给出的地址:退信会回到那里,接收方会把它记录成 Return-Path: 或者 Received-SPF: 里的 smtp.mailfrom=头部发件人是邮件内部的 From: 字段—— 唯一一个人类会看到的地址。SMTP 里没有任何机制要求这两者一致。

SPF 授权的是信封:发起连接的这个 IP,有资格为 MAIL FROM 的域发信吗?DKIM 授权的 是内容:发送方对一份自选的头字段清单(h=)加上一个正文哈希(bh=)做签名,并把 公钥发布在 DNS 里。两者都没对收件人读到的那个地址说过任何话。DMARC 通过要求 对齐来补上这个缺口——可见的 From: 域必须与 SPF 域或 DKIM 的 d= 域相符。一封 声称来自你银行的钓鱼邮件上出现 spf=pass 完全正常;出现 dmarc=pass 就不正常了。

第三件需要内化的事:这些校验都不是你在做。接收 MTA 在投递时刻跑完检查,然后把它的 判定写进一个 Authentication-Results: 头。此后,这个结果就只是一个文本文件里的 声明——只有当写下它的那一跳属于你自己的基础设施时才值得信任。一台陌生人服务器写的 头,完全可能是那个陌生人编出来的。从你的边界 MTA 往外读,并把它之外的一切都当作 不可信输入。

每种机制证明什么

机制发布位置认证的对象能否穿过转发能否阻止可见 From: 冒用
SPF信封域上的 TXTv=spf1 …MAIL FROM 所对应的发起连接 IP不能——中继 IP 未被列入不能
DKIM<selector>._domainkey.<d=> 上的 TXTh= 中的头字段 + 正文哈希 bh=能,除非正文或 Subject 被改动仅在对齐时能
DMARC_dmarc.<domain> 上的 TXTFrom: 与 SPF 或 DKIM 的对齐,外加策略能,靠存活下来的 DKIM能——这就是它存在的全部目的
ARCARC-Seal / ARC-Message-Signature跨中继保留上游的判定结果就是为此设计的不能——而且只有受信任的封章方才算
MTA-STS / DANE_mta-sts.<domain> 上的 TXT / TLSA传输层:TLS 是强制的不适用不能

三种检查共用同一套结果词汇,而每个词都指向不同的修法:

结果值含义常见原因
pass检查通过
fail未授权,硬失败(-all冒用,或者记录里漏了一个发信中继
softfail未授权,仅作标记(~all灰度期间记录还留着宽松策略
neutral明确表示不作判断(?all策略没有说出任何有用的东西
none没有发布记录该域从未配置过 SPF 或 DMARC
temperrorDNS 故障,可重试解析器或权威服务器抖动
permerror记录无法求值DNS 查询超过 10 次、重复的 v=spf1、语法错误

什么场景用哪个工具

如果你手上有原始头块——Gmail 里的“显示原始邮件”,别处的“查看源代码”——从 邮件头分析器 开始。它会把折行续行展开,把 Received: 栈反 过来排,让第 1 跳是最初的发送服务器、最后一跳是你的服务商,并显示相邻两跳之间相差 多少秒,于是 Delay: 1800s 就指名了那个把邮件压了半小时的中继。路径下方,它原样 打印 Authentication-ResultsReceived-SPFDKIM-SignatureARC-Authentication-Results——DMARC 的判定就藏在第一个里面,形如 dmarc=pass (p=REJECT …)。解析全程留在浏览器里,这正是关键:这些头携带发信方 IP 和内部主机名。

如果手上是一整个 .eml 文件——一封退信、一个存下来的附件、某个表单邮件程序的输出—— 就用 EML 邮件查看器。它把头和正文分开,解码 RFC 2047 编码字 (=?UTF-8?B?…?=),让非 ASCII 的主题以文字形式可读,并把 FromToCcSubjectDateReply-ToMessage-ID 摊出来。Reply-ToFrom: 处在不 同域,是商务邮件诈骗的经典特征。正文按原样展示,所以 MIME 边界保持可见;某个 base64 分段想读的话,扔进 Base64 编码 / 解码

只有域名、没有邮件时,就转到 DNS 上。DNS 查询工具 能解析 TXT (你的 v=spf1 字符串)、MX(路由,带优先级)等等,并给出决定一次修改要多久才 生效的 TTL——DNS 工具集 深入讲了缓存。它是服务端工具:域名会被 送到 api.sitekits.dev 去解析,且不被存储。它只接受主机名标签,所以 _dmarc.example.comselector._domainkey.example.com 这类带下划线的名字会被拒—— 那些请用 dig TXT _dmarc.example.com 读。

对可疑邮件里的链接,URL 解析和分析工具 会把一个 URL 拆成主机、 路径和解码后的查询参数,而且从不去请求它;IDN / Punycode 转换工具 则通过显示真正参与解析的 xn-- 形式来暴露同形字域名。两者都在浏览器里跑,也都被 收录在 /zh/for/security/ 上,和头分析器、.eml 查看器放在 一起。带外国时区偏移的跳时间戳,用 时区转换工具 更快对 齐——它同样是浏览器本地运行,但被归到 /zh/for/sre/ 而不是安全页。 把证据附到工单上之前,请手工脱敏——HAR 文件清洗器 覆盖的是 HAR 导出,不是邮件头。

通常会在哪里出错

p=none 什么都保护不了

p=none 只是索要报告。发布它是正确的第一步,但在你走到 p=quarantinep=reject 之前,接收方在失败时不会应用任何策略。也检查一下 pct=——部分灰度只会 对那个比例的邮件强制执行。

把十次查询的预算撑爆

include:amxptrredirect= 各自都要消耗 DNS 查询,而来自 SaaS 发信 方的嵌套 include: 链会递归地加上它们自己的那份。一旦超过十次,记录就变成 permerror,接收方会把它读成“完全没有 SPF”。把它展平,或者去掉不再用的厂商。

把每一次失败都当成攻击

转发和邮件列表会合法地破坏认证:中继 IP 通不过 SPF,而一个 [list] 主题前缀或追加 的页脚会让 DKIM 正文哈希失效。判断要看对齐关系和封章中继,而不是看单独一个 fail

相信不属于你的那些跳

只有在你的边界 MTA 处或之后添加的 Received:Authentication-Results: 行才有 意义;更早的跳都是攻击者可控的文本。算出来是负数或者显示 的延迟,是时钟偏差或 缺少时间戳,不是证据。

把不发信的域名放着不管

闲置域名以及从不发信的子域,同样需要 v=spf1 -all、一条位于 _dmarc.<domain>p=reject 的 DMARC 记录,最好再加一个 null MX(MX 0 .)。p= 是必填的策略标签: 一条只带 sp=reject 的记录是无效的,接收方什么都不会应用,闲置域名本身仍然毫无 防护。想把子域策略明确写出来时,在 p= 旁边加上 sp=reject——sp= 缺失时,子域 本来就继承 p=。放着不管,这个域名就是印着你品牌的免费冒用材料。

FAQ
只有 SPF 就足以阻止别人冒用我的域名吗?
不够。SPF 只授权信封发件人(也就是 SMTP 的 MAIL FROM),而收件人根本看不到它。攻击者完全可以在自己控制的域上让 SPF 通过,同时把你的域名写进可见的 From: 头里。只有 DMARC 通过要求 From: 与 SPF 域或 DKIM 域对齐,才把两者绑在一起;而且只有 p=quarantine 或 p=reject 才会让接收方在失败时真的采取动作。
一封正常的邮件被转发之后为什么 SPF 就失败了?
转发改变了发起连接的 IP,于是接收服务器拿转发方的 IP 去比对你的 SPF 记录,发现它没被列进去。DKIM 通常能存活下来,因为它签的是头和正文内容而不是路径——除非某个邮件列表改写了 Subject 或者追加了页脚,那会破坏正文哈希。这就是 DMARC 为什么接受两种机制中任一个通过,也是 ARC 为什么存在:它负责把原始判定跨可信中继带过去。
一个从不发信的域名还需要 SPF 和 DMARC 吗?
需要——一个不设防的闲置域名就是印着你品牌的免费冒用材料,而攻击者正是在找这种域。请发布 v=spf1 -all、在 _dmarc.<域名> 上放一条 p=reject 的 DMARC 记录,并且最好再加一个 null MX(MX 0 .),让入站邮件直接被拒。注意策略标签:p= 是必填的,所以一条只带 sp=reject 的记录是无效的,接收方什么策略都不会应用。在 p= 旁边加上 sp=reject 只是把子域策略明确写出来——sp= 缺失时,子域本来就继承 p=。
Authentication-Results 里的 dmarc=pass 到底意味着什么?
它意味着接收邮件服务器核实了 From: 头里的域与一个通过了 SPF 或 DKIM 的域相对齐,并应用了在 _dmarc.<域名> 上找到的策略。它是接收方事后把自己的判定写进邮件里的结果,不是一段你能从文本重新验证的签名。只有当写下它的那一跳属于你自己的边界 MTA 时,才可以信它。
为什么我的 SPF 记录报 permerror,而不是 pass 或 fail?
permerror 意思是这条记录无法被求值。常见原因是超出了 include:、a、mx 和 redirect= 所消耗的 10 次 DNS 查询上限,或者在同一个名字上发布了两条独立的 v=spf1 TXT 记录,又或者存在语法错误比如多了一个分号。接收方会把 permerror 当作“没有可用的 SPF”,于是 DMARC 策略就完全依赖 DKIM 了。