目录
背景
这两周做一个发邮件的需求,一波三折,期间既尝试了公司内部的发邮件服务,也尝试了调用 Gmail 个人邮箱的 API 来发送。对邮件服务的原理因此就有些好奇,上网搜了一些相关信息,整理如下。
(多说一句:刚工作那会儿我干过类似的事儿,当时是在一个创业公司,我想从家里登录办公室的电脑,但公司没有固定公网 ip,于是我写了一个 Shell 脚本,检测到公司路由器公网 ip 变化了就发一封邮件给我。从路由器到服务器之间配置了端口转发。)
什么是邮件头(Email Headers)
正常收发邮件的普通用户,是看不到邮件头的。邮件应用会默认隐藏邮件头。
但邮件头很有用。你可能已经注意到,在发邮件时,from
(发件人)字段是可以自行指定的,例如 Java 中是这样:
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("someone@somedomain"));
那么收信人怎么知道信中声称的发信人不是伪造的呢?这时候邮件头就派上用场了。
和数据包一样,邮件也不是直接从发件人那里发送到收件人那里的,而是在多个邮件传输代理(MTA,Mail Transfer Agent)中辗转数次,每一次都送到离收件人更近的 MTA 服务器上,最终送给收件人。
这期间的每一个 MTA 都会在邮件上加上一个 Header,最终在收件人那里能看到所有这些 Header,即邮件头(Email Headers)。
如何获取邮件头
一版在网页邮箱中,都能找到展示邮件头的相应选项。
Gmail 示例:
点击 Show original
之后,就能看到原始邮件了,原始邮件中最靠前的部分就是邮件头。
再以我最常用的新浪邮箱为例:
分析邮件头
首先把邮件头(或者整个原始邮件)粘贴到 VS Code,选择语言为 Email
。或者保存为 .eml
格式也行,VS Code 会自动识别:
这样就可以借助语法高亮,很清楚地识别邮件头的各个字段了。下面简单说几个常用的字段。
Header 是从下往上依次添加的,越靠下的部分越接近发件人,所以看的时候要从下往上依次看,弄清楚从发件人到收件人的链路。
Received
这是分析邮件头时最重要的一个字段。该字段告诉你邮件是如何一步一步经由各个 MTA 发到你手上的。
其中,每一次经由一个 MTA 都会加上一个 Received
字段,其中的 from
和 by
分别是发送的 MTA 和收取的 MTA。
例如:
Received: from rn2-txn-msbadger05101.apple.com (HELO rn2-txn-msbadger05101.apple.com)([17.111.110.85])
by sina.com (10.71.8.23) with SMTP
id 60E926D7000026EC; Sat, 10 Jul 2021 12:49:28 +0800 (CST)
其中,from rn2-txn-msbadger05101.apple.com (HELO rn2-txn-msbadger05101.apple.com)([17.111.110.85])
这行是说邮件在这一步是从这个 MTA 发出的。你可以根据域名、ip 等信息判断,这的确是从苹果公司的服务器发出来的;
by sina.com (10.71.8.23)
则说明,邮件被新浪收到了。这的确是一封苹果公司发到我新浪邮箱的邮件。
SPF(Sender Policy Framework)
SPF
是用来帮助判断发件人是否伪造的。正常情况下,SPF
取值应为 Pass
。例如:
Received-SPF: Pass (sina.com: SPF record at _spf-txn.apple.com designates 17.111.110.85 as permitted sender) identity=from;
DKIM(Domain Keys Identified Mail)
这也是用来帮助判断邮件发件人真实性的一个很重要的字段,可以简单理解为电子签名。DKIM 通常长这样:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=email.apple.com;
s=email0517; t=1625892495;
bh=mQkSp8cygTZ7zTrw7YuQofK1z6wuyCw6Yp1/eOBHFFc=;
h=Date:From:To:Message-ID:Subject:Content-Type;
b=Qis7zZOXz6vvVArRizpgnsJKO7iSUmeJ7QtEjeEQ5+4hv+KAEQCiPeyjcc6hqRrkJ
RFYvYH5fQjIGXvGnXhWtjE4J5QrhGOPbRfn3Yp2mhdo+d+JfvJTF6Nx2tD56jgu756
1+trUOezTshdsOv8XcXrGWBEag5mfPfTOjnfUy9W6Ec4wiEjFS0FYsl2e8eNOCi1Nu
EzLmhu3c/VGo1WNz5WYeoTP3LAJ/UZlqsFVxFoBRxUtTNPwBn/aIqmmA4mZnjkSwpb
gd2TCsweb3emVq+5ZfSrayemmyRNh/Yqobj1y21QGzrimlNKr4HmFG5ALQwo9DVT+F
VIl1PvL1td2lg==
我最近在做发邮件需求时就踩到了一个坑,怀疑是跟 DKIM
相关。我们调用公司的邮件服务,往公司内部邮箱发,本来发的很正常,结果突然有一天就怎么也收不到邮件了。
我们前后排查了大大小小各种原因,折腾了好久,最后(隔了一两天吧)收到一封系统通知才知道:邮件被拦截了!而且甚至都没出现在垃圾箱中。后来我在公司邮箱的隔离所中,果然找到了丢失的邮件。
之前怎么也想不到,有的邮件根本到不了你的垃圾箱中,就已经被拦截了。
这两天我分析了一下我们当初发的邮件的邮件头,发现甚至连这个 DKIM
字段都没有!
也难怪会被判断为垃圾邮件,给拦截了呢。