为什么主机上发出去的邮件有的邮箱收不到?为什么从主机上发送邮件会被当成垃圾邮件而进了 Spam?为什么会有人冒充你的域名发送邮件?这些问题的原因很可能是你没有 SPF 记录。

一、SPF 介绍

说来也神奇,互联网诞生至今已经八百年了(嗯?),但是目前互联网上发送电子邮件还是使用着历久弥新的「简单邮件传输协议」(SMTP),这协议在设计之初就没有考虑过多的安全性与防伪造性。但是那时候使用互联网的人都很纯洁啊,不会用 SMTP 来发垃圾邮件,但是现在的人不同了,由于 SMTP 可以不需要验证,所以随便找个主机,就能大量地向任何一个邮箱地址发送邮件了,垃圾邮件(Spam)就开始泛滥了。另外,发邮件的时候随便填写一下别人的域名,就可以冒充别的网站来发送邮件了,欺诈邮件(Phishing)也就开始泛滥了。

为了解决这种情况,OpenSPF 主导推出了 SPF。

Sender Policy Framework (SPF) is an email validation system designed to prevent email spam by detecting email spoofing, a common vulnerability, by verifying sender IP addresses. SPF allows administrators to specify which hosts are allowed to send mail from a given domain by creating a specific SPF record (or TXT record) in the Domain Name System (DNS). Mail exchangers use the DNS to check that mail from a given domain is being sent by a host sanctioned by that domain’s administrators.
Wikipedia.org

这是 Wikipedia 上对 SPF 的介绍,大意是说,SPF 是一种对发送邮件者的 IP 地址来进行简单的邮件验证的系统,域名的管理员通过 SPF 记录或 TXT 记录来规定这个域名下哪些 IP 地址是「允许」发送邮件的,这样如果有人用别的 IP 地址来伪造发送的话,收件方可以根据 SPF 来选择拒收这些邮件。

二、为什么要设置 SPF

相信各位读者也看出来了,这是需要发送方和接收方都配合才能生效的一套系统。如果发送方在 SPF 里拼命规定「某某 IP 不是我们的,如果从那里发邮件千万不要相信!」,但是接收方根本不去看 SPF 的话,那也是白搭。还好,很多国外的邮箱提供商都会去检查发送者域名的 SPF 的,一部分邮箱提供商如果发送发送者的域名没有 SPF 的话,有可能会拒收邮件,所以我们要给自己的域名设置 SPF!

三、SPF 语法格式

咳咳,相信细心的读者也发现了,上面说 SPF 是通过「SPF 记录」和「TXT 记录」来检查的。为什么会有 TXT 记录呢?这只是兼容性的考量:一部分老的 DNS 服务器不支持 SPF 记录,因此只能拿 TXT 记录来代替一下, OpenSPF 建议在这段过渡时期,SPF 记录和 TXT 记录都要添加……SPF 记录和 TXT 记录因此也是非常相近的:

wzyboy@vermilion:~$ dig wzyboy.im txt +short
"v=spf1 include:_spf.wzyboy.im include:_spf.google.com ~all"
wzyboy@vermilion:~$ dig wzyboy.im spf +short
"v=spf1 include:_spf.wzyboy.im include:_spf.google.com ~all"

嗯,记录类型不一样,记录内容是一模一样的……如果要在 BIND9 里设置的话,是这样的:

wzyboy.im. 3600 IN TXT "v=spf1 include:_spf.wzyboy.im include:_spf.google.com ~all"    
wzyboy.im. 3600 IN SPF "v=spf1 include:_spf.wzyboy.im include:_spf.google.com ~all"

还是比较容易理解的,如果使用第三方的 DNS 解析服务的话,也差不多是这样设置。

根据 OpenSPF 的介绍,我编造了一个 SPF 的示例:

v=spf1 mx a:pluto.example.net ip4:203.0.113.0/24 ip6:2001:DB8::/32 include:aspmx.googlemail.com -all

看起来很长的样子,其实很多成分是可选的,我只是尽量把它们都列出来而已,如果拆解看来看是这样的:

+ 或 – 或 ~ 或 ?
这是修饰符,可用在任何一个元素前面,如果不加的话,默认就是加号,代表「允许」;减号代表 hard fail,即「强制拒绝」;波浪线代表 soft fail,就是温柔地拒绝;问号代表 neutral,即「你看着办」。
v=spf1
这说明这是 SPF 第一版,必填。
mx
引入这个域名上的 MX 记录。可选。
a:pluto.example.net
引入 pluto.example.net 这个域名 A 记录所对应的 IP 地址。可选。
ip4:203.0.113.0/24
引入这个 IPv4 的地址段。可选。
ip6:2001:DB8::/32
引入这个 IPv6 的地址段。可选。
include:_spf.example.org
引入这个域名上的 SPF 数据。可选。
all
此项用来处理上面没有做过处理的的 IP 地址。可选。不用来发邮件的域名可以用 -all 来处理。建议在测试期使用 ~all,正式上线后使用 -all
redirect:_spf.example.org
这样写也是可以的,但是不能与上面的其他表达式共存,这是把整个域名的 SPF 交给另一个域名来处理。

上面这个例子中,我把尽可能多的 SPF 元素都列出来了,实际情况下,只要依据自己的情况,列出所有可能发送这个域名的邮件的 IP 地址,列为「允许」,再处理一下 all 的情况,就搞定了。不过需要注意的是,上面的表达式不能太多,一般 MTA 最多支持 10 次「展开」查询,超过了就悲剧了。

四、如何写 SPF

下面以某公司的几个网站为例说明一下如何设置 SPF。

该公司域名多、IP 多,且有些比较分散,因此如果要在每个域名下都设置 SPF,把一堆 IP 包括进去的话,如果以后 IP 有变动,改起来会非常麻烦,所以比较好的方法就是用上面所说的 include 语句,只维护一个 SPF 记录,然后别的域名上都去 include 它就好了。比如我们以 _spf.t.tt 这样一个简短好记的域名作为专门记录 SPF 数据的域名。那么就在 BIND9 中添加:

_spf.t.tt. 3600 IN TXT "v=spf1 ip4:192.0.2.128 ip4:198.51.100.128 ip4:203.0.113.0/24 ip6:2001:db8::/32 ?all"
_spf.t.tt. 3600 IN SPF "v=spf1 ip4:192.0.2.128 ip4:198.51.100.128 ip4:203.0.113.0/24 ip6:2001:db8::/32 ?all"

这是一张「允许 IP 列表」,把该公司所有的 IP 都包括进去了,注意最后是 ?all 因为这只是一张「允许」表,具体怎么用要交给每个域名单独设置。然后为该公司旗下的域名添加 SPF 记录:

t.tt. 3600 IN TXT "v=spf1 include:_spf.t.tt include:_spf.google.com ~all"
t.tt. 3600 IN SPF "v=spf1 include:_spf.t.tt include:_spf.google.com ~all"
linost.com. 3600 IN TXT "v=spf1 include:_spf.t.tt ~all"
linost.com. 3600 IN SPF "v=spf1 include:_spf.t.tt ~all"
xehost.com. 3600 IN TXT "v=spf1 include:_spf.t.tt ~all"
xehost.com. 3600 IN SPF "v=spf1 include:_spf.t.tt ~all"

由于 t.tt 这个域名还使用了 Google Apps 的邮箱服务,所以要把 _spf.google.com 这张表也 include 进去(这是 Google 的 SPF 数据),而另外两个域名不使用 Google Apps,所以就没必要把 Google 的加进去了,只加自己的就好了。

如果你的网站不是使用 BIND9,而是使用了第三方的域名解析服务的话,也照着以上的例子添加类似的数据就好了。如果有的名字服务(比如 DNSPod)不支持 SPF 记录的话,那就只能添加 TXT 记录了……

五、检查 SPF 是否生效

最简单的查看 SPF 的方法就是用域名查询工具了,比如:

wzyboy@vermilion:~$ dig t.tt spf +short
"v=spf1 include:_spf.t.tt include:_spf.google.com ~all"

这样就看到上文中设置的 SPF 记录了,的确存在 _spf.t.tt 和 _spf.google.com 这两张表,但是具体是否生效还是要从邮件头看。

从邮件服务器(IP 已经列为「允许」)以这个域名为 mailname 往 Gmail 发一封邮件,然后在邮件头中就能看到类似这样的信息:

Received-SPF: pass (google.com: domain of somebody@t.tt designates 1.2.3.4 as permitted sender) client-ip=1.2.3.4;

这就说明 SPF 记录的确生效了(注意,就像所有的 DNS 记录一样,SPF 记录生效也是需要时间的)。

好了,SPF 记录的设置就到这里结束了,咱们来欢乐一下吧:

wzyboy@vermilion:~$ dig _spf.google.com txt +short
"v=spf1 ip4:216.239.32.0/19 ip4:64.233.160.0/19 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:209.85.128.0/17 ip4:66.102.0.0/20 ip4:74.125.0.0/16 ip4:64.18.0.0/20 ip4:207.126.144.0/20 ip4:173.194.0.0/16 ?all"

Google 的 IP 真多啊……

本文地址 https://wzyboy.im/post/865.html ,转载请注明出处,谢谢合作。
感谢 Jimmy Xu 对本文的大力支持!


欢迎留下评论。评论前,请先阅读《隐私声明》。