Email 工具
邮件认证真正是怎么运作的:两个 From 地址的区别、SPF 与 DKIM 与 DMARC 各自证明什么、Received 链该怎么读,以及处理邮件头或 .eml 时该用哪个工具。
3 工具
两个 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 | 信封域上的 TXT,v=spf1 … | MAIL FROM 所对应的发起连接 IP | 不能——中继 IP 未被列入 | 不能 |
| DKIM | <selector>._domainkey.<d=> 上的 TXT | h= 中的头字段 + 正文哈希 bh= | 能,除非正文或 Subject 被改动 | 仅在对齐时能 |
| DMARC | _dmarc.<domain> 上的 TXT | From: 与 SPF 或 DKIM 的对齐,外加策略 | 能,靠存活下来的 DKIM | 能——这就是它存在的全部目的 |
| ARC | ARC-Seal / ARC-Message-Signature 头 | 跨中继保留上游的判定结果 | 就是为此设计的 | 不能——而且只有受信任的封章方才算 |
| MTA-STS / DANE | _mta-sts.<domain> 上的 TXT / TLSA | 传输层:TLS 是强制的 | 不适用 | 不能 |
三种检查共用同一套结果词汇,而每个词都指向不同的修法:
| 结果值 | 含义 | 常见原因 |
|---|---|---|
pass | 检查通过 | — |
fail | 未授权,硬失败(-all) | 冒用,或者记录里漏了一个发信中继 |
softfail | 未授权,仅作标记(~all) | 灰度期间记录还留着宽松策略 |
neutral | 明确表示不作判断(?all) | 策略没有说出任何有用的东西 |
none | 没有发布记录 | 该域从未配置过 SPF 或 DMARC |
temperror | DNS 故障,可重试 | 解析器或权威服务器抖动 |
permerror | 记录无法求值 | DNS 查询超过 10 次、重复的 v=spf1、语法错误 |
什么场景用哪个工具
如果你手上有原始头块——Gmail 里的“显示原始邮件”,别处的“查看源代码”——从
邮件头分析器 开始。它会把折行续行展开,把 Received: 栈反
过来排,让第 1 跳是最初的发送服务器、最后一跳是你的服务商,并显示相邻两跳之间相差
多少秒,于是 Delay: 1800s 就指名了那个把邮件压了半小时的中继。路径下方,它原样
打印 Authentication-Results、Received-SPF、DKIM-Signature 和
ARC-Authentication-Results——DMARC 的判定就藏在第一个里面,形如
dmarc=pass (p=REJECT …)。解析全程留在浏览器里,这正是关键:这些头携带发信方 IP
和内部主机名。
如果手上是一整个 .eml 文件——一封退信、一个存下来的附件、某个表单邮件程序的输出——
就用 EML 邮件查看器。它把头和正文分开,解码 RFC 2047 编码字
(=?UTF-8?B?…?=),让非 ASCII 的主题以文字形式可读,并把 From、To、Cc、
Subject、Date、Reply-To 和 Message-ID 摊出来。Reply-To 与 From: 处在不
同域,是商务邮件诈骗的经典特征。正文按原样展示,所以 MIME 边界保持可见;某个
base64 分段想读的话,扔进 Base64 编码 / 解码。
只有域名、没有邮件时,就转到 DNS 上。DNS 查询工具 能解析 TXT
(你的 v=spf1 字符串)、MX(路由,带优先级)等等,并给出决定一次修改要多久才
生效的 TTL——DNS 工具集 深入讲了缓存。它是服务端工具:域名会被
送到 api.sitekits.dev 去解析,且不被存储。它只接受主机名标签,所以
_dmarc.example.com、selector._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=quarantine 或
p=reject 之前,接收方在失败时不会应用任何策略。也检查一下 pct=——部分灰度只会
对那个比例的邮件强制执行。
把十次查询的预算撑爆
include:、a、mx、ptr 和 redirect= 各自都要消耗 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=。放着不管,这个域名就是印着你品牌的免费冒用材料。