邮件防伪造

邮件是Web服务最常用通讯和身份验证手段。但因为邮件协议本身的问题,导致邮件是可以被伪造的。通过规范化的设置,可以最大程度的防止邮件被伪造。

邮件为什么可以伪造

许多互联网服务的业务本质上是现实业务的类比和延伸,电子邮件也是如此。梳理一下现实中的邮局是如何对邮件进行管理的。假设北京王先生需要向上海钱女士寄一封信,那么王先生需要在信封上写清楚自己的姓名地址和别人的姓名地址,贴上邮票塞入邮筒。北京邮局收集邮筒,确认邮票并盖邮戳,通过不同的交通方式将邮件送到上海邮局。上海邮局确认邮戳并将邮件分拣派送到钱女士的邮箱里面。对比一下电子邮件的发送方式。假设gmail用户a-user要给outlook用户b-user发一封邮件,那么a-user应该登录gmail,写对b-user的邮箱地址,写上内容,点击发送。gmail邮箱服务打上自己的“邮戳”再使用发件协议将这封信发送给outlook邮箱服务,outlook邮箱服务确认“邮戳”后将邮件放到b-user的邮箱账户中。

这波操作是不是非常方便完美。但是,一般来说,公司会购买两类邮箱服务。第一类是员工使用的办公邮箱,用来给内外部邮箱收发办公邮件。第二类是给程序使用的通知邮箱,用来给用户邮箱发送验证码等通知邮件。通知邮箱只发送邮件,注重于短时间大数量的邮件发送。办公邮箱注重邮件收发及账号管理。这两类邮箱都会自定义域名作为邮箱后缀,如:@baidu.com。如果邮箱域名可以自定义,则出现伪造的问题。比如我可以自己搭建一个邮箱服务器,给指定邮箱发送一封伪造邮件。说自己是@baidu.com的,你中了五百万大奖,只需要转账一万块钱手续费。

可以伪造的原因在于,SMTP协议(邮件发送协议)是一个透明的协议。作为邮件发送者可以指定SMTP发送者的发送账户、发送账户的显示名称、SMTP服务器域名等信息,如果接受端未对这些信息进行认证,就可能放过一些刻意伪造的邮件。再次类比邮局业务可以看问题出在了哪。发件方邮局的邮戳,收件方已经知道是什么样子。收件方再收件的时候,只需要判断信封上的邮戳是不是真的即可。

收件服务商的防伪造验证策略

如果我拥有baidu.com这个域名的控制权,则可以在域名的DNS上加几条公开的TXT声明。声明里面写上,我发邮件的服务器IP地址是多少多少,可以验证我自己签名的公钥是多少多少。然后使用声明公钥相对的私钥签名邮件,再用声明的服务器发送邮件。收件服务器收到这封邮件后,看到是信封上说自己是baidu.com的,那好,收件服务器就去看看baidu.com这个域名上的公开TXT声明,拿公开的信息去验证一下这封邮件的真伪。如果IP地址或私钥签名对不上,则可以判定,这封邮件是仿照的。

现代的邮件服务器都有黑名单和反向认证等机制,如检查邮件来源IP、检查邮件发送域、反向DNS查询、登录验证等。伪造邮件是很难通过严格设置的邮件服务器的。

发送邮件的防伪造声明

对于声明安全信息的方法,业界标准是基于DNS的TXT标签。有SPF/DKIM/DMARC三种。如果想最大程度的防伪造,建议将三者全部配置上。其中,SPF和DKIM是并行的两种校验方式,同时是DMARC的基础。

DKIM

在 DKIM 流程中,公共密钥作为域 DNS 管理器(域的注册商或 DNS 提供商)的 TXT 记录来发布。每封外发的邮件包括使用私有密钥为特殊域生成的唯一签名。接收邮件服务器使用这个私有-公共密钥组合以验证邮件来源。如果验证失败,收件人服务器可能会基于服务器行为拒绝邮件或将它分类为垃圾/伪造邮件。

设置:

通用做法是增加TXT类型名为_domainkey的记录。值为邮件服务商提供的值。但为了区分自有服务,每个邮件发送服务商都提供了基于标准但是细节不同的配置方式。需要配置时需要按照自己服务商提供的教程进行配置。

SPF

SPF,全称为 Sender Policy Framework,即发件人策略框架。

设置:

TXT    @.example.com     v=spf1 include:spf.protection.outlook.com -all
TXT    *.example.com     v=spf1 -all
TXT    mail.example.com  v=spf1 include:mailgun.org -all

对于每一个子域名,都需要配置一个SPF记录。这里可以使用泛域名*设置所有子域名都是拒绝。然后在需要使用的子域名设置具体配置。注意泛域名*和不包含空主机头@

字段说明:

  • v: 版本(纯文本,必要)值一般为spf1,如果使用 Sender ID 的话,这个字段就应该是 v=spf2
  • include:使用给定的域名进行验证
  • ip4:使用 IPv4 进行验证。
  • -all:定义匹配时的返回值。
  • + 缺省值。在测试完成的时候表示通过。
  • - 表示测试失败。这个值通常是 -all,表示没有其他任何匹配发生。(建议设置为此值)
  • ~ 表示软失败,通常表示测试没有完成。
  • ? 表示不置可否。这个值也通常在测试没有完成的时候使用。

DMARC

DMARC是一种基于现有的SPF和DKIM协议的可扩展电子邮件认证协议,在邮件收发双方建立了邮件反馈机制,便于邮件发送方和邮件接收方共同对域名的管理进行完善和监督。

DMARC要求域名所有者在DNS记录中设置SPF记录和DKIM记录,并明确声明对验证失败邮件的处理策略。邮件接收方接收邮件时,首先通过DNS获取DMARC记录,再对邮件来源进行SPF验证和DKIM验证,对验证失败的邮件根据DMARC记录进行处理,并将处理结果反馈给发送方。

设置:

在DNS中增加TXT类型名为_dmarc的记录,值必须包含v和p两个字段

TXT    _dmarc.example.com         v=DMARC1; p=reject
TXT    _dmarc.mail.example.com    v=DMARC1; p=quarantine

建议将公司工作邮箱域名与程序发邮件邮箱域名分开。减少修改记录时出现误伤。如果不能分开,则一般使用组织域名绑定员工邮箱,二级域名绑定程序发送邮箱。此时两者都需要设置DMARC记录。同时组织域名的DMARC策略设置严格一点。

只设置v和p字段即可使用很安全的DMARC功能,如果需要其他需求可以增加其他字段。

字段说明:

  • v:版本(纯文本;必要的)值为“DMARC1”,必须作为第一个标签。
  • p:要求的邮件接收者策略(纯文本;必要的)表明接收者根据域名所有者的要求制定的策略。
  • none:域名所有者要求不采取特定措施
  • quarantine:域名所有者希望邮件接收者将DMARC验证失败的邮件标记为可疑的。
  • reject:域名所有者希望邮件接收者将DMARC验证失败的邮件拒绝。(不建议直接使用reject,会出现被拒收还查不出原因的风险。)
  • sp:要求邮件接收者对所有子域使用的策略(纯文本;可选的),若缺省,则“p”指定的策略将应用到该域名和子域中。
  • rua:发送综合反馈的邮件地址(逗号分隔的DMARC URI纯文本列表;可选的)如:mailto:dmarc@example.com
  • ruf:发送消息详细故障信息的邮件地址(逗号分隔的DMARC URI纯文本列表;可选的)如:mailto:dmarc@example.com

一些说明

收件方查询DMARC策略

接收方找到发件人地址(From)的DNS记录。请求该域名下的DMARC记录,如果则去请求该域名的组织域下的DMARC记录。

//在服务商处购买的,可以独立操作的域名可以看做是一个组织域,如: 
noreply@example.com的组织域是example.com 
noreply@abc.example.com的组织域是example.com 
noreplay@abc.def.ghi.example.com的组织域是example.com 
noreplay@abc.example.com.hk的组织域是example.com.hk

测试工具

mac上可以使用dig命令,如

dig -t txt example.com

spf在线检查工具

https://www.kitterman.com/spf/validate.html

收件客户端为什么会显示代发

在邮件标准中,存在mail_from和from两个概念。如果这两者不一致, 则收件客户端会显示代发, 用以提醒收件人两者的不同。

mail_from: 是信封上的发件人,由[前缀@域名]组成,是实际发件人 
from: 信封内容里的发件人。 也就是我们平时看到的

遇到过一些坑:

  • DNS中有NS记录和SOA记录。NS记录表示域名服务器记录搜索,用来指定该域名由哪个DNS服务器来进行解析;SOA记录设置一些数据版本和更新以及过期时间的信息。soa记录中需要关注的点是最后一条字段,minimum_time_to_live,表示缓存否定TTL。如果该域名的子域名被删掉,dns在访问不到该子域名时,会记录这个ttl时间。在该ttl时间内,即使将该子域名新增回来,dns系统中仍然显示找不到。一般来说,国内dns服务商基本不允许个人修改SOA记录。但是使用国外dns解析时,需要注意该值,并尽可能将该值设定成小于等于600

  • 一个可见域名上不要有多条spf记录。(mail.example.com和example.com是两个可见域名)