多数安全补丁一旦以修正后的二进制程序投入运行,风险便会下降。Debian 在 2008 年发布的 OpenSSL 修复范围更窄:它阻止系统继续生成可预测的加密材料,却修复不了此前已经生成的 SSH 主机密钥、用户凭据、VPN 密钥、DNSSEC 密钥或证书私钥。在有人找到并替换这些秘密之前,它们始终可以被猜中。[1][6]

这也解释了 CVE-2008-0166 为何会成为开源软件史上影响长久的事件。一个微小的下游改动不只削弱了算法,还把秘密生成过程缩减为一组可由攻击者预先计算的有限结果。即时补丁很直接,真正的恢复工作则涉及资产清点和信任关系追踪,范围覆盖近二十个月里生成的密钥。[3][4]

图片背景:DebConf 7 合影摄于 2007 年 6 月,正值受影响时段的中段。画面呈现了一个整合其他项目代码的发行版背后有多少人。照片本身没有记录缺陷;它记录的是社区和下游维护环境,以及一次看上去只影响局部的清理如何产生全系统后果。[2][7]

diff 很小,语义变化却很大

事情始于对调试告警的清理。2006 年 4 月 19 日,Debian 缺陷报告 #363516 指出,OpenSSL 随机数生成器引发的 Valgrind 告警会扩散到调用该库的应用程序。在讨论中,Debian 维护者 Kurt Roeckx 找到 crypto/rand/md_rand.c 里的两处 MD_Update(&m, buf, j),并考虑将其注释掉。软件包后来的改动消除了这些嘈杂的读取告警,但两个外观完全相同的调用中,有一个处在另一种语义环境里:它会把经随机池输入路径送入的字节混入生成器状态。[2]

这一区别贯穿了整起事件。同一份缺陷日志后来的技术说明分别解释了 ssleay_rand_addssleay_rand_bytes:前者经 RAND_add 系列函数触发,后者在应用程序请求输出时触发。从前者移除混入输入的调用以后,应用程序自认为加入随机池的数据再也影响不到池状态。代码照常运行,密钥看起来依旧像密钥,Valgrind 也安静了下来,三种表象之下的安全约定却已经改变。[2]

软件包时间线还为事后分析埋下了一处陷阱。最初尝试的改动出现在 0.9.8b-1,当时修改的文件尚未进入实际编译。直到 Debian 于 2006 年 9 月 17 日上传 0.9.8c-1,改动后的文件被移入真正生效的构建路径,存在缺陷的行为才开始出现。它一直留在 Debian 衍生系统中,直至 2008 年 5 月 13 日发布安全公告和修复软件包:Etch 对应 0.9.8c-4etch3,testing 与 unstable 对应 0.9.8g-9。[1][2][4]

把问题称作“OpenSSL 依赖未初始化内存”,并未说清失效机理。存在缺陷的版本移除了一条重要输入路径,生成器中真正有用的变化主要受可预测的程序行为,以及 RAND_bytes 混入的进程 ID 支配。只要掌握程序的调用次序、平台表示方式和 PID,输出就可以复现。密钥依旧来自一台确定性机器,只是这台机器失去了本应用来让每次运行都不可知的随机种子;因此,故障没有表现为某个一眼可见的不安全值直接充当密钥。[3][5]

随机性失效变成了搜索问题

Debian 的历史响应页面用少见的具体数字说明了问题规模。在该页面考察的常见平台类别中,损坏的生成器在每种架构上只有 32,767 条由 PID 派生的流。将小端 32 位、小端 64 位和大端 32 位系统合在一起,一共只有 98,301 条基础流。与加密密钥空间相比,这个数字小得惊人。[3]

真实的密钥生成还包含这套简化模型没有纳入的更多条件。在独立测量研究中,Scott Yilek 及其同事针对 32,768 个 PID 值、三类平台和 OpenSSL .rnd 文件的三种状态生成候选密钥。对每一种受测密钥长度,他们都得到了 294,912 把密钥;测试范围从常见的 512 位到 8,192 位 RSA 密钥,也包括调查中见到的非标准长度。[5] 密钥长度仍可标作“2048 位”,实际的不确定性却取决于已经严重贫化的生成器状态。

候选密钥一旦可以提前生成,攻击便不再像一场艰难的因数分解。攻击者可以把服务器公钥与目录逐一比对,匹配成功后直接取出早已掌握的私钥部分。Debian 和 Ubuntu 系统受到直接影响,但影响范围取决于密钥来源,并不取决于系统眼下运行何种操作系统:弱密钥复制到其他平台以后,弱点仍会保留。[1][6]

影响也超出了长期使用的 RSA 密钥。Debian 列出的受影响用途包括 SSH、OpenVPN、DNSSEC、X.509 材料和 SSL/TLS 会话密钥。对于 DSA,它划出了更严格的范围:只要 DSA 密钥曾在存在缺陷的系统上用于签名或认证,就必须视为已经泄露,即便密钥最初在别处生成,因为可预测的单次签名秘密会暴露私钥。GnuPG 和 GnuTLS 密钥没有卷入这次故障,因为它们没有使用受影响的 OpenSSL 生成器。[1][3]

更新软件包仍关不上事件

安全公告给出的修复顺序十分重要。先安装修正后的 OpenSSL 软件包,再从头生成受影响的加密材料。[1] 颠倒顺序会让替代密钥继续从同一个损坏的随机源中产生。只完成第一步,则会让所有长期使用的弱密钥继续留在系统里。

因此,完整的恢复工作更接近一次凭据迁移,超出了常规漏洞补丁的范围:

  1. 从每一台会生成替代材料的系统中移除存在缺陷的生成器;
  2. 找出受影响时段内生成的密钥,包括已经导入其他系统的副本;
  3. 使用修正后的软件栈生成新材料;
  4. 向每一个依赖方分发新的公钥和证书;
  5. authorized_keys、信任存储、VPN 配置、DNS 区域和服务端点中删除旧条目;以及
  6. 验证旧凭据确实会遭拒绝,不能只看正常流程是否已经停止使用它们。

Debian 把有限的密钥全集转化成一项运维控制措施。它发布了弱密钥黑名单和 OpenSSH 工具,可识别已知指纹、拒绝弱用户密钥,并替换受影响的主机密钥。响应页面还记录了分别覆盖常见密钥长度和额外密钥长度的软件包,并按应用给出轮换说明。[3] 这是一项有针对性的事件工程设计:利用可预测性找出部分已经造成的损害。

但它无法成为普遍适用的安全证明。历史黑名单的覆盖范围取决于密钥类型、长度、架构、程序行为,以及实际生成过的候选集合。Debian 同一页面也解释了为什么某个 PEM 文件或非默认密钥路径没有匹配结果,仍不足以证明其来源清白。[3] 已知密钥是在受影响系统上生成时,直接替换比“未在这份清单中找到”更能说明问题。拒绝清单可以确认密钥已经受损,覆盖不完整的清单无法证明密钥安全。

善后按证书轮换的速度推进

一项独立的长期研究说明了这一区别为何会影响实际处置。Yilek 及其同事每天扫描超过 50,000 台启用 TLS 的 Web 服务器,发现其中 751 台仍在出示含有漏洞的证书。在披露四天后仍有漏洞的主机中,30% 到将近六个月后依然没有完成修复。研究人员还发现,公开披露以后,证书颁发机构仍在为弱密钥签发证书。[5]

这条漫长的尾部正是有状态事件的特征。整个服务器集群的软件包替换可以自动完成,身份材料的替换却会跨越更多负责人和故障域。Web 密钥会涉及新的证书请求和 CA 签发;SSH 主机密钥变更会在客户端触发警告,还需要经过认证的沟通渠道;用户密钥会被复制到数十个账户;VPN 或 DNSSEC 轮换既会因过早移除旧信任而中断,也会因旧信任长期有效而留下风险。

从外部看得到的服务端点,也会遮住只完成了一部分的工作。服务器已经打好补丁,却仍在出示旧的弱证书;它也会在出示强度足够的替代证书时,让弱密钥继续留在其他位置的授权清单中;还有的服务器会围绕同一个旧公钥申请新证书。这些状态都不会反映在“OpenSSL 软件包已经是当前版本”这句话里。只有把生成器、生成产物和每一个接受凭据的位置当作同一个系统处理,事件才算关闭。

真正失效的环节

第一处失效发生在跨越下游界线的语义审查。两行源码外观相同,内存检查工具对两者都提出异议,软件包改动也改善了一个局部质量信号,然而两个调用分别属于不同的数据流。用视觉对称来审查加密代码尤其危险:一项操作的含义,取决于数据是在作为输入收集、作为状态变换,还是作为输出返回。[2]

第二处失效,是测试了可见症状,却没有测试安全属性。有效的回归测试除了检查 Valgrind 是否安静、openssl genrsa 是否完成,还应验证显式种子输入能否改变生成器后续输出,以及在 PID 受到控制时,各次独立运行能否保持不可预测。对于安全敏感代码,干净运行只能说明表面执行正常,不能证明熵保持完整。

第三处失效是资产核算。存在缺陷的软件包有明确的负责人和版本,由它生成的密钥却没有共用同一份登记册。这些密钥散落在用户主目录、Web 服务器配置、证书数据库、路由器、VPN 对端、DNS 基础设施、备份,以及早已不再运行 Debian 的系统中。软件物料清单追踪到二进制程序便告结束,事件的扩散却从那里开始。

2026 年的运维经验

把 2008 年的黑名单摆在今天的 OpenSSL 旁边,解决不了下一次生成器故障。软件运维应当在故障发生以前,就把秘密来源和替换流程纳入日常工作。

对于维护者和发行版,即便下游补丁的名义目的只是可移植性、消除告警或软件包维护,只要涉及加密状态转换,就应明确交由上游审查。测试应覆盖 RAND_add 一类输入与独立输出所必须保持的安全属性,不能只检查 API 调用是否成功。发行版与上游之间的代码差异,也是安全产品的一部分。

对于平台团队,长期使用的密钥需要附带元数据:生成它的服务或主机、库与镜像版本、算法、创建时间、负责人、部署位置、到期时间和吊销途径。有了这些记录,“哪些秘密来自这个组件”便从考古项目变成一次查询。小型团队可以用一份严格维护的秘密资产清单记录这些信息;大型服务器集群则需要把它们接入签发流程和机器身份管理。界线很明确:元数据若无法找出每一个接受凭据的位置,就还算不上一份轮换计划。

最后,事件演练应区分预防和根除。修正版阻止新的弱产物出现;检测找出已知的坏产物;轮换生成替代材料;吊销移除旧有权限;验证则证明旧密钥已经失效。少了其中任何一个动作,仪表板即使全绿,凭据仍会继续可用。

Debian 熵缺陷令人难忘,是因为它把这套生命周期完整呈现了出来。私钥在数学意义上很长,在运维系统中四处散落,却仍然可以枚举。响应人员不再把软件包视作事件的全部,转而沿着它的输出追到每一个仍然信任这些密钥的位置,修复才最终完成。

来源

  1. Debian 安全团队,“DSA-1571-1: New openssl packages fix predictable random number generator”,2008 年 5 月 13 日——原始安全公告,包含受影响版本、密钥类型和重新生成指南。
  2. Debian 缺陷跟踪系统,缺陷 #363516,“valgrind-clean the RNG”——2006 年的维护讨论、软件包时间线,以及后来对两处 MD_Update 所在语义环境的说明。
  3. Debian Wiki,“SSLkeys”——历史技术概要,包含 PID 派生流的数量、弱密钥黑名单、覆盖范围限制和各应用的轮换说明。
  4. Debian 安全跟踪系统,“CVE-2008-0166”——受影响的 OpenSSL 版本范围和 Debian 修复软件包版本。
  5. Scott Yilek、Eric Rescorla、Hovav Shacham、Brandon Enright 和 Stefan Savage,“When Private Keys Are Public: Results from the 2008 Debian OpenSSL Vulnerability”,ACM Internet Measurement Conference,2009 年——独立实证研究及作者托管的论文。
  6. CERT 协调中心,“VU#925211: Debian and Ubuntu OpenSSL packages contain a predictable random number generator”,2008 年 5 月 15 日——独立的影响分析和修复说明。
  7. Aigars Mahinovs,“Debconf 7 Group Photo”,Wikimedia Commons——2007 年 6 月 21 日摄于爱丁堡的真实照片,也是本文配图。