安全事故与漏洞
Heartbleed(CVE-2014-0160)——加密通信的根基泄露了内存的事件
2014 年,支撑全球 HTTPS 通信的 OpenSSL 中发现了会把服务器内存内容泄露到外部的漏洞 Heartbleed。连私钥、会话信息都可能被窃取的严重性、为何会发生的原理图解、时间线,以及『一旦泄露就按全部泄露处理』『更换密钥与证书』这一至今未变的教训,本文用图与表为你讲解。
回顾过去的重大事件,只聚焦于对当下你的运维真正有用的教训。我们不复现攻击,而是从防御的视角来解读。
发生了什么——「返回的比请求的多」
TLS(HTTPS 的加密)中有一种用于维持连接的「存活确认(心跳)」交互。客户端发送一小段数据和一个「把这个返回给我」的长度,服务器照样返回——本来这只是个如此简单的机制。
OpenSSL 的实现没有校验这个申报的长度。明明实际只发送了几个字节,却申报「返回 64KB」,服务器就会超出收到的数据量,把相邻的内存也一并读取并返回。如果那里恰好存着私钥或会话,它们也会一起泄露。
正常:发送的长度=返回的长度
「返回 "bird"(4 个字符)・长度 4」→ 服务器「bird」
Heartbleed:短发送+大申报长度
「返回 "bird"(4 个字符)・长度 64KB」→ 服务器「bird+相邻的内存 64KB(可能包含私钥、会话等)」
攻击方不需要任何特殊权限。通过反复多次操作,就能一点点收集到内存的碎片。根基中的根基——TLS 库里的一个小 bug,动摇了全世界通信的信任。
“不留痕迹的泄露”之可怕
Heartbleed 不易在常规的访问日志中留下痕迹,事后很难断定「是否泄露了」「泄露了什么」。一旦难以判断,按最坏情况行动才是正解。
时间线
2014-04-07
漏洞被公开,修复版 OpenSSL 同时发布。公开后不久
全球服务器一齐着手打补丁。影响范围之广引发一片哗然。此后
许多服务按「私钥已泄露」的前提吊销并重新签发证书,并呼吁用户更改密码。
为什么「只打补丁」并不算结束
既然私钥有可能已经泄露,那么即便打了补丁,过去泄露的密钥仍然有效。所以应对要分两步走——堵住漏洞(打补丁)+按已泄露的前提更换资产(密钥、证书、机密)。
不充分的应对
- 更新 OpenSSL 后就认为「已经修好了」
- 只更换确认被利用的那一个机密
- 证书继续照常使用
正确的应对
- 在更新之外,还要吊销并重新签发证书(重新生成私钥)
- 彻底更换服务器持有的机密、密钥、密码
- 督促用户更改密码
对当下有用的教训
按『一旦泄露就是全部泄露』处理
不是只换已确认的那一个,而是彻底更换服务器持有的机密、密钥、密码。面对不留痕迹的泄露,按最坏情况行动才安全。
吊销并重新签发证书
既然私钥有可能已经泄露,证书也要重新生成。只要不轮换密钥,过去的泄露就会一直存活下去。
把“根基软件”也纳入监控对象
不只是应用,像 OpenSSL 这样的根基软件的 CVE 也要用机器监控来追踪。越是根基,影响范围越广。
理解内存安全的价值
「读取的比请求的多」这类 bug,可以通过内存安全的设计与语言从结构上减少。把它纳入选择根基软件的考量。
接下来阅读
- 术语:什么是 CVE
- 事故:.env 公开导致全部机密泄露的故事
- 入门:如何守护机密·超入门
FAQ
QHeartbleed 可能泄露了什么?
服务器的内存内容。运气不好时,可能包含 TLS 私钥、会话内容、登录信息等。而且这种泄露不易留下痕迹,事后很难确定『到底泄露了多少』,这正是棘手之处。
Q为什么会发生『返回的比请求的多』?
在维持连接的存活确认(心跳)中,服务器没有校验对方申报的『希望返回的长度』就直接信任了。明明实际只发送了很短的数据,却申报『返回 64KB』,服务器就会把相邻的内存一并读取并返回。信任了输入(长度)正是漏洞所在。
Q应对中最重要的是什么?
打补丁之后要『按已泄露处理』。具体而言,就是吊销并重新签发 TLS 证书,以及彻底更换服务器持有的机密与密码。仅仅修复应用是不够的。