
2014年4月7日OpenSSL项目组在官网挂出一个安全公告编号就是CVE-2014-0160。当时很多人没当一回事可接下来的48小时全球互联网公司几乎都在熬夜打补丁安全圈直接炸锅。这个漏洞后来有了一个非常形象的名字心脏滴血。如果你平时维护网站或者只是偶尔用OpenSSL生成证书、转换证书、检查证书这篇文章都能帮你彻底搞懂它到底是什么、危害有多大、现在该怎么排查和预防。我会尽量用大白话讲尽量不堆术语保证你看完也能给同事讲明白。1. 先认识OpenSSL加密界的老黄牛1.1 OpenSSL到底在忙什么要弄懂心脏滴血先得知道OpenSSL是干什么的。简单说它是目前互联网上用得最广的一个开源加密库主要帮我们实现HTTPS那套加密通信。你在浏览器地址栏看到的小锁头背后就是SSL/TLS协议在工作而OpenSSL就是实现这个协议的最主流工具。你可以把OpenSSL想象成一个“加密快递站”。客户端和服务器之间要传包裹OpenSSL负责给包裹加锁、验证对方的身份、然后安全地派送。它既能给数据加密也能给别人发来的密文解密还能生成证书、校验证书、转换证书格式。市面上的Nginx、Apache这些Web服务器以及大量邮件服务器、路由器、防火墙底层都在用OpenSSL。也正因为它是底层基础设施大部分时候你根本感觉不到它存在。可是这种“感觉不到”恰恰最危险因为这栋楼的每一根承重梁里都浇筑了同一批水泥水泥出了问题楼里每一间房都跑不掉。OpenSSL一旦出漏洞受影响的绝不是一个两个网站而是一大片互联网服务。1.2 为什么OpenSSL一炸半个互联网都睡不着心脏滴血漏洞公布当天安全圈的状态基本可以用“手忙脚乱”来形容。原因很简单OpenSSL的使用范围实在太大了。从大型网站到企业内部系统从云服务器到网络打印机很多设备的Web管理界面都内置了OpenSSL。我举一个直观的例子。当时很多主流Linux发行版默认自带的OpenSSL版本都中招你只要用的不是最新版很可能就是漏洞版本。更要命的是很多设备是嵌入式系统比如路由器、防火墙、硬盘录像机之类它们没法像普通服务器那样轻松执行升级命令甚至有些厂家早已不更新固件了。这就意味着哪怕官网发布了修复版大量设备依然长期暴露在漏洞之下。还有一个容易被忽略的点OpenSSL是开源项目代码一直公开可查但当时愿意认真审计它的人太少。它代码量巨大、历史包袱重、函数命名晦涩安全研究价值又极高所以一旦发现漏洞往往是“发现一个就是一个大的”。心脏滴血就是典型例子它是最低级的逻辑疏忽却因为所处位置太核心直接炸穿了一层信任体系。1.3 OpenSSL证书原理为什么总有人问证书转换聊OpenSSL就绕不开证书。HTTPS之所以安全关键在于数字证书。证书里包含网站的公钥、域名、有效期、签发机构等信息。服务器把证书发给浏览器浏览器用证书里公钥去验证服务器的身份同时协商出加密密钥。这个过程中OpenSSL扮演的角色既像办证机构的工作人员又像门卫负责出示和核验证件。很多人第一次接触OpenSSL并不是因为要搞懂加密原理而是因为遇到证书相关问题。比如你拿到一个.pem文件但操作系统需要.crt或.der格式这就得用OpenSSL做证书转换。再比如你在服务器上配置HTTPS后浏览器提示“unable to get local issuer certificate”这就是证书链验证失败说明服务器没把中间证书发全。这些日常操作其实都和OpenSSL的证书体系有关。理解了这个背景再回头看CVE-2014-0160你就明白为什么一个内存读取漏洞能让整个行业冷汗直冒因为它泄露的东西里很有可能就包括私钥。私钥一旦泄露证书体系再健全也白搭。2. 心脏滴血漏洞是怎么滴血的一次不怀好意的“心跳”2.1 Heartbeat扩展本来是干嘛的心脏滴血这个漏洞出在TLS协议里的一个扩展功能上叫Heartbeat也就是心跳。它的用途特别朴素让通信双方确认对方还活着。打个比方你和一个朋友约好每周五通一次电话确认彼此还安好。这就是心跳。在TLS连接里如果客户端和服务器长时间不通信中间的网络设备可能会把连接回收掉。为了不让连接断掉就可以定期发一个“心跳包”服务器收到后原样回复一个“心跳包”双方就都知道连接还在。这个设计本身没有错问题是实现它的代码出了问题。Heartbeat消息里有两个关键字段一个是实际发送的数据内容另一个是声明这段内容的长度。服务端收到消息后会按照对方声明的长度去内存里读取数据然后把读到的内容原样返回。正常情况下你能控制好自己的语速把100个字的内容准确传达给对方。可如果对方谎报说自己发了1000个字而服务端又完全信任这个声明它就会自己去内存里翻出1000个字来念给你听。多念出来的那900个字就根本不是你的东西了。2.2 用一个生活例子看懂漏洞全过程我再换个更直白的场景。你去银行柜台取款递进去一张取款单上面填的取款金额是10000元。柜员本来应该核对单子上的金额和你的实际意图是否一致但这家银行的柜员不核对只看单子上的金额直接从金库里拿10000元给你。心脏滴血漏洞就是这个取款逻辑只不过它发生在内存里。客户端发一个“心跳请求”里面写我的数据长度是65535字节请把这个长度的数据回给我。但实际上客户端只发了几个字节。服务端一旦傻傻地按照65535字节去读内存就会把内存里其他数据一起打包返回给攻击者。更糟糕的是服务端返回的这段数据就像是你翻别人抽屉时看到的东西可能有正在处理的其他用户的用户名密码、Cookie、加密密钥、证书私钥甚至你刚提交的聊天记录。攻击者可以反复发起这种请求每次偷一点凑出一个完整的信息拼图。因为内存里的数据是动态变化的多试几次总能捞到有价值的东西。2.3 攻击者到底能捞到什么级别的秘密很多人会问一次才能读64KB的数据是不是危害不大这么想就太乐观了。64KB是单次请求的上限攻击者完全可以发起成千上万次请求来反复读取内存。我在做安全测试时见过最典型的泄露内容包括其他用户正在传输的请求明文、登录后的Session ID、内部API返回的数据甚至在某些极端情况下把服务器的私钥也给读了出来。Session ID相当于你登录状态的临时令牌拿到了它攻击者就能直接冒充你登录网站。而私钥泄露就更严重这意味着攻击者可以解密所有经过这台服务器的历史流量也可以伪造一个看似有效的证书来钓鱼。所以这个漏洞真正可怕的地方并不在于它每次泄露多少而在于它泄露的数据可能极其敏感并且攻击门槛极低。当时开源社区很快就写出了利用脚本不需要什么高深技术拿到IP就能跑这才是它被称为“核弹”的原因。2.4 为什么偏偏叫“心脏滴血”这个名称来自英文Heartbleed是个双关。漏洞出在Heartbeat心跳扩展上数据又像伤口流血一样一点一点往外渗所以叫“心脏滴血”。这个名字起得非常精准因为在实际攻击中每次读取到的内存数据带有很多无效垃圾信息真正有价值的数据是夹杂在里面的就像血从伤口慢慢渗出来一滴滴接着一滴滴。本质上看这是一个非常低级的疏忽。开发者忘了校验实际数据长度和声明长度是否一致导致服务器无条件信任外部输入。这个漏洞在2012年3月被引入OpenSSL代码库直到2014年4月被修复整整存在了两年多。这两年多里有没有人已经悄悄利用过它没有人能给出准确答案这才是最让人脊背发凉的地方。3. 影响范围与历史复盘被低估的“互联网核弹”3.1 哪些版本中招哪些版本安全我先给你一张对照表看完你对是否受影响会一目了然。情况OpenSSL版本说明受影响1.0.1 到 1.0.1f2012年3月引入漏洞直到2014年4月修复安全1.0.1g首次修复版本安全1.0.1 后续版本1.0.1h及以上均已修复安全1.0.0 分支不含心跳代码不受影响安全0.9.8 分支不含心跳代码不受影响所以判断起来并不难你只要运行openssl version看到版本号在1.0.1到1.0.1f之间那就基本可以确定在漏洞范围内。这里要注意某些Linux发行版会自行给OpenSSL打补丁但版本号可能没变所以最可靠的方式还是参考发行版自己的安全公告。3.2 当时互联网到底震了多大心脏滴血公布后不少第三方机构做了全网扫描最后得出了一个让人倒吸凉气的数字全球大约有50万到100万台服务器用了受影响版本。更麻烦的是很多设备并不直接暴露在公网但内网攻击者只要扫描到相关端口就能利用。当时的处理潮有多夸张很多大型网站和技术团队直接在社交平台刷屏提醒用户尽快修改密码。印象里有一段时间安全圈的朋友见面第一句话不是问“吃了没”而是问“你升级OpenSSL了没有”。不少公司连夜给所有服务器打补丁、吊销旧证书、重新签发新证书那几天CA机构的压力也非常大。最难受的是那些用了受影响版本但又无法快速升级的嵌入式设备。比如一些老型号的路由器、IP摄像头、存储设备厂商停更后漏洞就永远留在那了。这也给大家提了个醒物联网设备不是装好就不用管了固件和底层库的安全一样重要。3.3 CVE-2016-2183和心脏滴血的异同这几年搜索“Windows服务器如何修复OpenSSL信息泄露漏洞”时经常会搜到CVE-2016-2183这个编号。我第一次看到时也疑惑过以为又出了个心脏滴血。实际上CVE-2016-2183是另一个漏洞它也属于OpenSSL的安全问题但和心脏滴血不是同一个成因。简单说CVE-2016-2183涉及的是3DES等旧加密算法因为使用64位块存在碰撞风险攻击者如果捕获大量加密流量可能推断出部分敏感信息。它同样属于信息泄露类但利用门槛比心脏滴血高得多危害也不是一个量级。不过在修复思路上两者高度一致升级OpenSSL到修复版本、禁用有问题的旧算法、更新配置项。修复完心脏滴血后再处理CVE-2016-2183本质上就是在做同一件事把不再安全的加密标准替换掉。安全运维就是这样你得习惯和各种各样的编号打交道。4. 小白自查实操5分钟判断你的服务器中没中招4.1 先看本机OpenSSL版本如果你有服务器登录权限第一步永远是看版本。在终端输入openssl version看到类似OpenSSL 1.0.1f 6 Jan 2014这种输出你就要高度警惕了。这个版本正好在受影响范围内需要立刻升级。如果看到OpenSSL 1.0.1g或者更高的版本号那基本可以放心。不过这里有个坑有些发行版虽然修改了代码却保留了原始版本号没动导致openssl version看起来还是老版本但实际已经修复了。所以更稳妥的做法是登录你所用Linux发行版的官网查一下它对应的OpenSSL安全公告确认是否打过修复补丁。4.2 用nmap远程探测自己的服务器没有服务器登录权限时你也可以用nmap这样的小工具做一次远程体检。nmap很早就集成了心脏滴血检测脚本用法是这样的nmap -p 443 --script ssl-heartbleed 你的服务器IP或域名如果输出里有VULNERABLE字样说明目标服务器存在漏洞。如果显示Not vulnerable则表示没有检测到问题。这个脚本是通过实际发送心跳数据包来验证的所以结果相对可靠。但要提醒一句这个命令只应该用在你自己拥有、或者你被授权测试的服务器上。安全问题不是用来搞破坏的先拿到授权再动手检测这是底线。对别人的服务器随便扫既可能违规也可能给自己惹麻烦。4.3 如果中招了最快能怎么修最快的修复方式是在系统包里把OpenSSL升级到修复版本。以Ubuntu/Debian为例sudo apt update sudo apt upgrade openssl libssl-dev如果是CentOS/RHEL系sudo yum update openssl升级完成后一定要重启依赖OpenSSL的服务比如Nginx或Apachesudo systemctl restart nginx或者sudo systemctl restart apache2不重启的话旧进程还带着老版本的OpenSSL在跑等于补丁白打。注意升级只是第一步。还记得前面说的吗这个漏洞可能已经泄露过私钥如果你怀疑私钥已经被盗光升级是不够的必须重新生成密钥、重发证书这一步不能跳过。5. 修复与善后升级OpenSSL只是第一步5.1 普通用户该做的三件事如果你只是普通网民看到心脏滴血这类新闻没必要慌但一定要做三件事。第一尽快修改重要账号的密码尤其是邮箱、网银、社交账号。虽然很多网站早就修复了但密码这东西定期换没坏处。第二关注你常用网站发布的官方公告大网站通常会在修复后提醒用户。第三能开两步验证的账号就开加了这道锁就算密码泄露攻击者也很难登录。有人可能会问密码管理器有没有用我个人建议长期用因为它能让你每个网站都用不同的复杂密码就算某个网站泄露也不至于牵连其他账号。心脏滴血这类漏洞提醒我们你依赖的平台再大也可能出问题账号安全绝不能只靠网站单方面保护。5.2 服务器管理者必须完成的完整善后流程如果你负责服务器这组操作请按顺序走每一步都不要省。升级OpenSSL并重启Web服务。生成新的私钥命令参考openssl genrsa -out newkey.pem 2048。密钥优先使用2048位以上。用新私钥生成证书请求CSRopenssl req -new -key newkey.pem -out new.csr。把CSR提交给CA机构重新签发证书。别嫌麻烦为了防范私钥泄露只能这么做。拿到新证书后先在公司内部用openssl verify -CAfile ca.pem server.crt验证证书链再替换到服务器上。重启Web服务确保HTTPS正常。去CA系统里吊销旧证书同时向证书透明度日志系统提交相关信息。强制所有用户修改密码让旧的Session ID全部失效。这一步很重要因为Session ID可能已经泄露了。第2到第7步是很多人容易遗漏的。我见过不少团队升级了OpenSSL就以为万事大吉结果私钥其实早就被读走了。心脏滴血的可怕之处就在于你可能根本察觉不到它有没有被利用过。所以只能当作“已经被利用”来处理果断重新生成密钥、重发证书。5.3 证书转换和证书链报错这两件高频事得学会处理完漏洞你大概率还会碰到另一个高频问题就是证书格式转换。Heartbleed是2014年的旧事但openssl x509 -in cert.pem -outform der -out cert.der这种转换命令到今天依然是搜索热词。最常见的需求是把PEM格式转成DER或把DER转成PEM。命令很简单# PEM转DER openssl x509 -in cert.pem -outform der -out cert.der # DER转PEM openssl x509 -in cert.der -inform der -out cert.pem比格式转换更烦人的是证书链验证失败。报错信息里那句“unable to get local issuer certificate”十有八九是服务器只配置了网站证书没把中间证书一起配进去。浏览器拿到网站证书后要往上找签发者结果中间证书没有自然就验证失败了。解决办法是把网站证书和中间证书合并成一个文件然后配置给服务器。合并时顺序很重要一般是自己的证书放最前面然后是中间证书顺序反了同样报错。用OpenSSL的cat拼接命令或者直接把中间证书内容追加到证书文件末尾都可以解决。5.4 Windows服务器修复信息泄露类OpenSSL漏洞的思路很多人搜索“Windows服务器如何修复CVE-2016-2183”说明Windows上的OpenSSL问题确实困扰了不少运维。Windows服务器通常有两条路一是有系统补丁直接更新二是软件自带的OpenSSL版本比如某些Web中间件、邮件服务、数据库客户端把OpenSSL静态编译进了程序里这种就得等软件厂商发新版。我处理过不少Windows服务器发现很多程序版本没更新OpenSSL还是老的。遇到这种情况你得先弄清楚这个程序用的是系统OpenSSL还是自带OpenSSL。如果是自带的你大概率不能直接替换掉它目录里的libssl.dll因为程序可能依赖特定版本强行换会导致崩溃。正确做法是去软件官网找修复版本重新安装。不管是CVE-2014-0160还是CVE-2016-2183修复思路都一样升级、换配置、验证。Windows下升级完后建议用一个在线检测工具反复扫描几次对外端口确认漏洞真的不存在了再收工。6. 踩坑经验与日常安全习惯6.1 这个漏洞为什么藏了两年没人发现心脏滴血给安全行业最大的教训就是不能盲目信任外部输入。客户端说数据长度是多少服务端就真的去读多少这是最典型的“信任用户输入”酿成的悲剧。放在写代码场景里也很好理解。你写了一个接口接收用户传的数组长度然后按照这个长度去遍历数组。如果这个长度没做上限校验用户传个超级大的值程序就会越界读取内存。OpenSSL的代码里当初就是没对Heartbeat消息的payload长度做校验才埋下了这个雷。这也解释了为什么开源软件需要持续做安全审计。眼睛看得见的地方人往往会仔细检查但越是“理所当然”的逻辑越容易被忽略。像这种基础库一旦出问题就是全球性事件所以后来很多公司都加强了对自己依赖库的代码审计和漏洞监测不再把安全寄托在“应该没问题”上。6.2 OpenSSL日常使用中的几个高频坑抛开历史漏洞OpenSSL在日常使用里也有很多容易踩的坑。我把这些年遇到的高频问题整理一下希望能帮你避开。第一个是随机数生成命令。openssl rand -hex 32这个命令很容易理解就是生成32字节的随机数并用十六进制显示。很多人拿它生成密钥、Token或者初始化向量用法没问题。但要注意如果是在脚本里频繁调用而系统熵不足生成的随机数可能没有想象中那么“随机”。所以对安全敏感的场景建议用系统自带的/dev/urandom或者用OpenSSL的rand命令加熵源参数。第二个是网上搜“openssl salted__ 在线解密工具”时常有人疑惑解密工具怎么识别不出加密内容。实际上你用openssl enc加密文件时默认会在输出文件头部加一个固定标记Salted__后面跟8字节盐值。这个标志不代表文件损坏也不代表加密方式的元信息它只是告诉解密程序这个文件用了盐。解密工具识别出Salted__其实是很正常的解不开多半是密码不对或者算法指定错了。第三个是OpenSSL和SHA2的关系。OpenSSL本身不是算法它是算法和协议的一个实现库SHA2是一组哈希算法的统称。你可以写代码调用OpenSSL库去计算SHA256也可以直接用命令行openssl dgst -sha256 文件来算文件的SHA256值。很多人在证书签名、文件完整性校验时用到它但在生成和验证时要保证算法一致否则会得到完全不同的结果。第四个是安装OpenSSL时的小问题。很多Linux发行版其实默认就带OpenSSL你不需要去源头官网手动编译。Windows上如果想用建议直接下载官方Windows安装包不要用来源不明的第三方编译版因为你无法保证里面没被改过。6.3 学会读懂官方安全公告看完这篇你应该对CVE编号不陌生了。以后在服务器上发现OpenSSL版本老旧可以去官网或各大Linux发行版的安全公告页面查相关CVE信息重点关注这几个字段CVE编号、影响版本、修复版本、CVSS评分、公告发布日期。我自己的习惯是每季度给服务器做一次安全巡检其中“检查OpenSSL等核心系统库的版本并核对官方公告”是固定动作。新漏洞发布后即使暂时没有补丁也要把风险记下来等修复版本一出就安排升级窗口。千万别等到被扫描到漏洞了再来补救那时候可能已经晚了。这个习惯的养成其实就是从心脏滴血那天开始的。以前我总觉得漏洞离自己很远但看了这个全球性漏洞的影响范围之后我才意识到安全不是一次性的动作而是持续运转的流程。你维护的不只是几台服务器而是信任你的那批用户。现在再看CVE-2014-0160它已经成为一个历史符号提醒着每一代人基础软件的安全容不得半点侥幸。如果你看完这篇文章发现自己管理的服务器还停在旧版本不用犹豫按我前面写的升级和善后流程走一遍这是对自己也是对用户负责。