案例分享:一封邮件如何影响整个邮件服务的解析

2019-04-30

想象一下,您正在运行一个电子邮件服务器......鉴于上述情况,您需要做的第一件事就是:设置邮件的大小限制。

显然,邮件越大,它被服务器接收的可能性就越小。

因此,为了将解析时间控制在合理范围内,您可以将单封邮件的体积上限限制为20MB。 籍此,您就可以放心地使用那些经过实战考验的、时下流行的邮件解析器。

为了简化邮件解析器的复杂性,您可以直接将处置20MB电子邮件时的CPU使用率作为参考基线准,以保证自己的服务器能够每秒处理数千封邮件。

因此,假设服务器处理相同大小邮件的时间就是恒定的,那么您只需要8GB的内存,便可足够应对每秒200封且大小为20MB的邮件并发量了。

有了这样的简化场景,您后续只需要考虑带有0字节的附件,即空文件的安全危害即可。

相关概念请参见:。

下面是一个简单的范例:如何发现此类漏洞回到开始提到的案例,我曾经遇到过:由于一个奇怪的入栈邮件,突然导致了V8(一种JavaScript引擎)的垃圾收集器一次性地阻止了的事件循环长达几十秒。

我为此花费了两天的时间,每隔几分钟就去重置feature-flags,以减少邮件队列的负荷。 最后,在VyacheslavEgorov(请参见)的帮助下,我注释掉了V8的CollectAllAvailableGarbage函数(请参见)。 该函数的内部工作原理是:对那些巨大的(几个GB大小)堆栈随机进行七次收集。 由此,我所吸取到的教训是:应当谨慎地对堆栈进行对象分配,进而避免对事件循环的阻断。

从去年年初开始,我不断在开源社区--上编写并更新自己的邮件解析器。 我的目标是:希望新的版本能够具有更快的解析速度、更少的资源分配数、能够在原始的缓冲区上运行、以及对RFC具有100%测试覆盖率(包括模糊测试,fuzztests)。

在此过程中,我进一步了解到:策略决策会比邮件服务器本身更有利于邮件的解析;同时,邮件的解析也会反过来促进策略的决策。

此处的策略决策包括:拒绝明显的恶意代码,拒绝各种损坏的、或被截断的Base64、以及Quoted-Printable之类的字符编码,拒绝重复性的关键标题(请参见),限制multipart的数量,以及限制由于对multipart边界的误报而引起的回溯。 今年初,我与《避免阻断事件循环的完全指南》(,请参见)一文的作者--JamieDavis取得了联系。 Jamie在文中所讨论的如何抵御事件循环攻击,正是我在本文中提及的,针对邮件解析器的multipart攻击。

九点改进建议在此,我为大家列出了针对此类问题的九项值得尝试的最佳实践:攻击对于资源稀缺的系统更容易产生效果。 作为知识的积累,您可以通过《mechanicalsympathy》()一文,来了解底层硬件是如何运作的,以及如何通过编程,实现与底层硬件的良好协作。 由于解析的算法既会涉及到CPU的使用,又会涉及到内存的分配,因此我们需要事先合理地配置好硬件资源。 如果您的代码能够有效地使用CPU、内存、磁盘、及网络的话,那么您可能就不太会碰到资源匮乏的问题。

当然,您仍需要对所有的系统资源,进行合理的使用限制。

2.在设计之初,就从不同的资源维度进行粗略的背板计算。 此法能够尽早地暴露并发现设计中的缺陷,进而避免产生那些不可能的解析。

由于系统的性能和安全性通常很难通过后期的优化而有所改进,因此它们需要在初期就被规划好,而不要等到用户使用量上去了,才亡羊补牢。 3.平衡所有维度上的资源使用情况。 不要出现:您虽然尚有足够的CPU去满足吞吐量的需求,但早已耗尽了内存的情况。

因此,您同样需要通过粗略的背面计算,来保持各类资源的使用占比,以避免产生各种设计中的潜在瓶颈。

4.记住:在运行事件循环时,大多数性能问题都源自拒绝服务式的等待。

因此,如果有用户报告性能问题,那么您首要检查的应该就是安全方面的攻击可能性。 5.验证所有用户的入栈数据,不仅要考察单位时间的数据量,还应当检查一段时间的总量。 某些攻击往往会以潜移默化的方式,对您的邮件系统进行逐步渗透,然后产生倍数效应,并最终接管您的系统。 6.注意模块边界之间的空白地带。

不要依赖其他的开发伙伴去帮助您弥补这些不足之处。 为了避免策略决策上的缺陷,您应当更好地了解代码间的依赖关系。 7.从整体的安全角度出发,制定严格的过滤策略,对于不确定是否健康的邮件,系统应当坚决拒之千里之外。 8.不要只是从开发人员的角度去检查自己的代码,而需要从恶意攻击者的角度出发,考虑他们会如何利用那些邮件解析中的代码漏洞。

在程序发布之前,请在每个模块中至少仔细地检查并修复三个漏洞。 9.编写简单的模糊测试用例(请参见),以随机生成各种有效的和无效的参数。

针对某个函数相对其他函数的返回值,请测试其有效性、正确性、以及各种无效的异常输出。

您可以根据Linus极端法则(请参见),运行具有数百万个参数组合的函数模糊测试。 私有与公共披露时间表该漏洞已于2018年4月23日向受影响模块的所有者进行了披露。

不过,就在90天的公开披露截止日期到期之前,该所有者以某种理由推迟了对它的公开披露。

此后,通过联系与之相关的依赖项模块(主要是在GitHub上),该漏洞已于2018年6月25日得到了全面公开与披露。

另外,有关这五大类邮件解析器的DoS漏洞介绍和具体信息,请参见下表:1.()。