Poketto

多出来的一个 goto fail;,让 iPhone 和 Mac 的 HTTPS 校验形同虚设

目录

文章主题概念插画

配图:AI 生成的概念示意图,非论文原图、真实现场或实验数据。

TL;DR:2014 年 2 月,苹果修复了 Secure Transport 中的一个严重漏洞(CVE-2014-1266):TLS 握手里一行重复的 goto fail; 让服务器签名校验被直接跳过。攻击者在同一网络中可冒充 HTTPS 服务器,窃听或篡改数据。这是一个确认存在并已修复的真实漏洞;至于有没有人在现实中大规模利用,本文所依据的资料并未给出结论。

这是 2014 年的旧事,不是今天的新闻。2014 年 2 月 21 日,苹果发布 iOS 7.0.6,随后在 2 月 25 日发布 OS X 10.9.2,修复了这个后来被称为「goto fail」的漏洞。Google 工程师 Adam Langley 次日(2 月 22 日)发表了一篇技术分析,成为最常被引用的解读。

一行代码出了什么问题

出问题的函数是 SSLVerifySignedServerKeyExchange,位于苹果的 Secure Transport 库。TLS 握手时,服务器要用证书对应的私钥,对临时密钥参数签名,客户端据此确认「对面真的是证书的主人」。

函数里是一连串类似「出错就 goto fail;」的检查。但其中一处写成了这样:

if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;

第二个 goto fail; 没有花括号,也不属于 if,缩进只是看上去像。于是代码每次都无条件跳到 fail: 标签,而此时 err 仍是 0,也就是「成功」。后面真正验证签名的步骤被整个跳过。

结果是:攻击者可以出示一条合法的证书链,却用别的密钥签名,甚至不签名,客户端照样放行。由于证书链本身是有效的,证书固定(pinning)也拦不住它。

影响范围

按 Langley 的分析和苹果公告,受影响的是 OS X 10.9.2 之前的 10.9.x(该漏洞在 10.9 中引入)、iOS 7.0.6 之前的 iOS 7,以及 iOS 6.1.6 之前的部分 iOS 6 版本。凡是使用 Secure Transport 的软件都会中招,比如系统自带的 Mail;Chrome 和 Firefox 使用的是 NSS,不受影响。漏洞出现在 DHE/ECDHE 类密码套件的握手路径上,TLS 1.2 走的是另一个验证函数,不受影响。

为什么说荒诞

一个只需一行重复代码就能破坏的安全检查,居然躲过了代码审查和测试。Langley 认为这是流程问题:缺少针对这种非法握手的测试,编译器也有能发现不可达代码的警告(如 Clang 的 -Wunreachable-code),但它并不包含在 -Wall 里。另外,这类 if 不加花括号的写法,也常被拿来作为反面教材。

后续

补丁发布后,用户的应对很简单:升级系统。在补丁出来前,Langley 给出的临时办法是让客户端只启用 TLS 1.2 或只用纯 RSA 套件。这件事之后,「if 一定要加花括号」成了不少团队的编码规范,静态分析和异常握手测试也更受重视。

来源:

关注这个空间,它的新文章会出现在你的关注动态里。