以 Heartbleed 为起点
原文由 Salvatore Sanfilippo 于 发布,订阅该博客
最近 OpenSSL 漏洞引发的强烈反应是可以理解的:当整个互联网突然都需要打补丁时,确实不是什么愉快的事。而且就我个人而言,这个漏洞如此低级,更让人不安。我无意指责 OpenSSL 的开发者,但在像 OpenSSL 这样的软件中,你通常会觉得这类问题应该更隐蔽一些。通常是边界检查做得*不正确*,而这个漏洞则是在 memcpy() 调用中*完全没有*做边界检查。 不过,有时早上起来重读前一晚自己写的代码,我也会感到十分羞愧。程序员总会犯错,我自己就经常犯,所以我认为,需要改变的是流程,而不是换掉 OpenSSL 团队。 有人提议改用比 C 更安全的语言,也有人认为问题出在规范本身过于复杂。两种说法或许都有一定的道理,但短期内我们不太可能更换规范或系统编程语言,所以真正的问题是,我们现在能做些什么来提升系统软件的安全性? 1) 砸钱。 只要有投入,让系统代码更安全其实很简单。如果多家公司聘请安全专家对 OpenSSL 代码库进行代码审计,那么发现类似 Heartbleed 这样的漏洞的概率就会更大。 我见过一些非常复杂的漏洞,需要满足一系列非平凡条件才会触发,却能通过认真的代码审计被发现。像这种没有边界检查的 memcpy(),只要是从安全的角度去审读代码,第一遍就会非常显眼。而 Heartbleed 又是怎么被发现的呢?正是通过 Google 进行的安全审计发现的。 或许,把开源仅仅当作索取对象的时代已经过去了。许多公司应该效仿 Google 等公司的做法,投入人力来参与开源软件的开发和安全工作。 2) 静态和动态检查。 静态代码分析在某种程度上,是一种半自动化的代码审计方式。 对于 OpenSSL 这类关键的系统代码,哪怕只是为了让静态分析更有效而对源码做一些标注或采用一套规则,也是完全值得的。 如今的静态分析工具还远非完美的解决方案,但如果由经验丰富的程序员仔细检查静态分析的输出,依然能带来一定的价值。 另一大助力来自 Valgrind 这类动态检查工具。每一个用 C 编写的系统软件,都应该在每次提交后自动使用 Valgrind 进行测试。 3) 用库来抽象 C。 C 语言贴近底层,语言本身没有任何内置的安全性。不过 C 的一个好处是,它允许你在其原始特性之上构建抽象层。 一个设计良好的动态字符串库就能避免大量的缓冲区溢出问题,如今几乎所有像样的项目都在使用这类库。但你还能做得更多。例如,对于可能包含私钥等敏感数据的关键安全代码,你可以扩展动态字符串库,加入只在不同缓冲区之间拷贝时进行隐式合法性检查的内存拷贝原语。 此外,如果某个缓冲区包含关键数据,你可以为其设置逻辑权限,一旦尝试从该区域拷贝数据就直接让程序中止。还有一些可移植性较差的方法,可以通过内存管理以更有效的方式保护重要的内存页,不过出于可移植性和可预测性的考虑,在 C 层面提供更高层次的保护在现实中往往要简单得多。 总的来说,为了避免在毫无防护的情况下直接使用 C,可以探索很多方法,通过在其之上构建抽象库来让编程更安全。 4) 随机化测试。 单元测试很难触发边界情况和未通过的合法性检查。 有一类已经存在数十年的测试方法,在我看来远远没有得到充分利用,那就是模糊测试。 只要发送各种带有不同随机参数的 OpenSSL 数据包,并结合 Valgrind 这类动态分析工具,这个 OpenSSL 漏洞是完全可以被发现的。 根据我的经验,大量的随机化测试,加上让同一套测试在 Valgrind 环境下反复运行,能够发现许多在其他情况下会被忽略的真实漏洞。可探索的模式有很多,通常你既需要注入完全随机数据的模式,也需要将合法数据包以各种随机方式加以破坏的中间模式。 这种技术的一个典型例子是早年的 DNS 压缩无限循环漏洞。向一个简陋的实现丢几包随机数据,几分钟内就能把它找出来。 5) 转变对安全性与性能的观念。 有意思的是,OpenSSL 之所以自己做了一套分配缓存机制,是因为在某些系统上 malloc/free 很慢。这表明即便在安全至关重要的代码中,性能至今仍被看得过重,甚至超过了安全性。就这个具体例子而言,必须承认,OpenSSL 开发者在封装 malloc 时很可能根本没考虑过这样做的安全影响。然而,他们会在*某些*系统上如此在意分配函数这类底层细节,本身就说明了对性能的深切关注,而他们本应更深切地关注系统的正确性和安全性。 总体而言,无助于现状的是,作为当今服务器基础设施事实标准的系统,也就是 Linux,一直以来拥有、且至今仍拥有你能找到的最差的分配器之一,很大程度上是出于许可证方面的顾虑,因为更优秀的分配器并非 GPL 许可,而是 BSD 许可。 这或许又是大公司应该做出贡献的另一个领域——对 glibc malloc 进行重大改进。即便存在更好的替代品,现实中许多系统软件最终使用的仍会是 glibc 的 malloc。 我希望关于 Heartbleed 的讨论能采取更务实的态度,因为有一点可以肯定:无论指责哪一方,都不会改变 OpenSSL 或其他任何软件实际的安全水平,而未来还有新的挑战。例如,HTTP/2.0 的实现就可能是一个安全上非常微妙的时刻。 编者注:其实我之前说错了,Glibc 中的 malloc 实现是 BSD 许可的,所以并非许可证问题。我也不清楚为什么 Glibc 不改用 Jemalloc,那是一个非常优秀且仍在积极维护的分配器。
随机一篇博客
评论
登录后参与讨论