跳转到内容

Heartbleed — 一个忘了写边界检查的 bug 让全网 1/3 的 HTTPS 站点漏内存

是什么

2014 年 4 月 7 日,OpenSSL 这个全世界至少一半 HTTPS 网站底层用的加密库爆了一个洞,编号 CVE-2014-0160,外号 Heartbleed(心脏出血)

这个洞让攻击者只发一条精心构造的 TLS 心跳包,就能让服务端把**自己进程内存里随机一段(最多 64KB)**回吐给攻击者。这块内存可能装着用户密码、session、甚至最致命的——HTTPS 私钥本身。

这篇 IMC 2014 论文不是发现这个洞的论文(洞是 Codenomicon / Google 几乎同时发现的),而是全网测量这个洞的影响

  • 漏洞披露当天,全网有多少站点中招?
  • 站点多快打完补丁?长尾有多长?
  • 大家有没有重发证书、有没有撤销旧证书?
  • 有没有人在披露之前就在野外利用?

日常类比:心脏出血像银行金库的密码锁有个出厂 bug——任何人按一组特殊键就能让锁吐出最近用过的几张钞票。Durumeric 这帮人干的事,是事后 6 个月走遍全国所有银行,统计:哪些没换锁、哪些换了但没销毁旧钥匙、哪些到现在还没听说这事。

为什么重要

不读这篇,就没法理解下面这些事:

  • 为什么 2014 年之后 Google 突然 fork 了 OpenSSL 做 BoringSSL,OpenBSD 做 LibreSSL
  • 为什么 Linux 基金会成立 Core Infrastructure Initiative 给 OpenSSL 这种”全世界都在用但没人付钱维护”的项目注资
  • 为什么 Rust 替换 C 写底层这件事在 2014 年之后才真正被严肃讨论
  • 为什么”漏洞披露”本身是一门有方法论的学问——光修了代码不够,还要主动通知所有运营商
  • 为什么直到今天(披露 12 年后)还能在 IoT 设备和老旧内网里扫到中招的机器

这篇论文的另一层意义是测量驱动的安全研究范式:用 ZMap 全网扫 + Bro IDS 历史日志 + A/B 通知实验,把一个抽象的”漏洞响应”问题量化成数字。

核心要点

1. 漏洞本身:少了一行边界检查

OpenSSL 实现 TLS 心跳扩展(RFC 6520)时,代码大致长这样:

// 简化版,删掉无关部分
memcpy(response, request->payload, request->payload_length);

问题:request->payload_length 是攻击者自己声称的长度(最大 64KB),但 request->payload 实际数据可以只有 1 字节。memcpy 信攻击者的话,从内存里多读 64KB 拼到回包里。

这块多读的 64KB 是OpenSSL 进程的堆内存,里面可能刚处理过别的用户请求,残留着私钥、密码、cookie。

bug 在 2012 年 3 月被引入,潜伏 2 年零 1 个月才被发现。

2. 全网测量:1/3 的 HTTPS 站点中招

作者用 ZMap 在披露后 24 小时内扫遍 IPv4 全网的 443 端口,结果:

  • Alexa Top 100 万 HTTPS 站点里 24%-55%(不同算法)中招
  • 全网 IPv4 里约 360 万台 443 服务器有响应,其中相当比例脆弱

3. 修补长尾:2 个月后还有 3% 没修

  • 24 小时内:大站(Google / Facebook / Yahoo 等)已经打完补丁
  • 48 小时:Top 1000 里基本完事
  • 2 周:Top 1 万收敛
  • 2 个月:Top 100 万里仍有约 3% 未修
  • 更长尾:嵌入式设备、老 IoT、内网不联外网的盒子,5 年后扫还在中招

4. 证书响应的尴尬

光打补丁不够,因为补丁前的私钥可能已经被偷了。正确流程是:补丁 → 重新生成私钥 → 申请新证书 → 撤销旧证书

实际数据:

  • 大量站点重发了新证书 但忘了撤销旧的——攻击者拿着偷来的旧私钥在 OCSP / CRL 没拒绝它之前可以一直用
  • 少数站点甚至用同一把私钥重发证书——等于啥也没干

5. 通知实验:主动告知能加速 50%

作者把脆弱站点的运营商分成两组:

  • A 组:通过 WHOIS 抽 abuse@ 邮箱,主动发邮件告知”你这台机器中招了”
  • B 组:什么都不做(对照组)

结果:A 组打补丁速度比 B 组快约 50%,差异显著。这给后续大规模漏洞响应提供了实证依据——主动通知是值得做的

6. 披露前没人在野外利用

用 Bro IDS 在多家高校 / 企业的历史日志里回溯,披露日(4 月 7 日)之前没看到 Heartbleed 攻击模式;披露后几小时内全网扫描爆发。

这意味着发现者没有抢在公开前被坏人利用——但下一次未必这么走运。

实践案例

你今天能做什么

如果你在做后端 / 运维 / 安全:

  1. 你的依赖里还有 OpenSSL 1.0.1 到 1.0.1f(含)的版本吗?理论上 12 年前就该升了,但老固件、老内网设备没准还有
  2. 证书撤销机制你的栈支持吗?OCSP must-staple / CRLite / CRL 至少要有一个
  3. 私钥泄漏后有没有 SOP 走”换私钥 + 重发证书 + 撤销旧证书”三步?很多团队只走第二步

测量论文的方法论

如果你想做类似研究,三件套:

  • 主动扫描:ZMap / Masscan,单机几十分钟扫完全 IPv4 一个端口
  • 被动日志:Bro/Zeek IDS 抓协议元数据,日志保留几个月
  • 干预实验:A/B 通知、补丁推送、配置变更,量化效果

踩过的坑(论文也踩过的)

  1. 扫描的伦理:往全网每台机器发心跳包”试一下漏不漏”本身是灰色地带。作者用 abuse-friendly 的发包频率 + 反向 DNS 表明研究意图 + 可联系到的 opt-out 邮箱
  2. 测量偏差:CDN 后面的站点你扫到的是 CDN 不是真站;多个域名指向同一 IP 时计数会重
  3. 通知实验的伦理:A/B 意味着 B 组明知中招也不通知。作者最终给 B 组也补发了通知,只是延迟一段
  4. 私钥真的能偷出来吗:理论上能,但实际取决于内存布局。Cloudflare 当时搞过一个公开挑战赛,几个研究者在几小时内偷出来了,证明可行

长期影响(2014-2026)

  • OpenSSL fork 大潮:LibreSSL(OpenBSD,2014-04)、BoringSSL(Google,2014-06)相继诞生,砍掉历史包袱
  • Core Infrastructure Initiative(2014-04):Linux Foundation 牵头,给 OpenSSL / OpenSSH 等关键基建注资,雇全职维护者
  • Rust 替换 C 的呼声主流化:rustls 项目崛起,Cloudflare、AWS 等大厂在边缘组件用 Rust 替 OpenSSL
  • Certificate Transparency(CT)普及:所有公网证书必须上 CT 日志,泄漏的私钥更容易追踪
  • 撤销机制改进:OCSP must-staple 标准化,Mozilla 的 CRLite 把整张吊销列表压到客户端

适用 vs 不适用

这篇方法论适用

  • 任何”已公开漏洞 + 大规模部署”的场景(Log4Shell 2021 / Spring4Shell 2022 都被复用过)
  • 全 IPv4 一个端口扫得完的协议(HTTPS / SSH / DNS)
  • 能从 WHOIS / abuse-c 找到运营商联系方式

不适用

  • 内网漏洞——扫不到
  • IPv6——空间太大扫不完
  • 应用层漏洞(需要登录 / 状态)——单包探测办不到

学到什么

  1. C 的边界检查 bug 可以潜伏数年——单元测试 / fuzz / code review 都没抓住,最后是 Codenomicon 用专门 fuzz 工具找到的
  2. 漏洞响应是有长尾的——大站 24h,小站几个月,长尾设备永远不修
  3. 主动通知有效——这个结论后来被 Log4Shell 等事件反复验证
  4. 测量改变讨论方式——之前”漏洞披露后多快被修”是凭感觉,从此有了数字
  5. 关键基建的悲剧:OpenSSL 这种被几十亿设备依赖的库,2014 年只有 1.5 个全职维护者

延伸阅读

关联

  • [[tls-1.3]] —— 同一协议族,2018 年标准化时大量删除”危险扩展”
  • aes —— Heartbleed 泄漏的私钥用来解密 AES 会话,威胁链的下一环
  • akamai-2002 —— CDN 是 Heartbleed 响应里最快打补丁的一类基础设施
  • libsignal —— 端到端加密的 Rust 实现,是”用 memory-safe 替 C”的代表