我指的是开发者世界中的全球“我们”。
一开始有SSL
当我在 20 世纪 90 年代中后期第一次了解 SSL 以及如何使用它时,我花了一段时间才意识到并理解让客户端在握手中验证服务器证书的至关重要性。一旦我理解了,我们就确保curl会默认正确地进行检查,并在证书检查失败时拒绝连接。
从curl和libcurl 7.10(2002年10月发布)开始,我们默认验证服务器证书。二十二年后的今天,实际上应该没有用户使用默认情况下不验证证书的curl 版本。
正在验证什么
验证 TLS 服务器证书的标准方法是 A) 检查它是否由受信任的证书颁发机构 (CA) 签名,B) 证书是否是为您交互的对象创建的;该域名已在证书中列出。
或者,您可以选择“固定”证书,然后验证该证书是否与特定哈希相对应。这通常被认为更脆弱,但避免使用验证服务器证书的数字签名所需的“CA 存储”(“您”信任的证书颁发机构的一组证书)。
跳过意味着不安全
跳过证书验证会使连接不安全。因为如果您不进行验证,就没有什么可以阻止中间人位于您和真实服务器之间。或者甚至只是假装是真正的服务器。
挑战
如果您尝试在开发环境中使用生产站点的证书,则可能会使用不同的名称连接到服务器,然后验证失败。
如果您有一个活跃的中间人拦截并想要窥探 TLS 流量,则它需要提供不同的证书,除非该证书可以由您信任的 CA 签名,否则验证将失败。
如果您的 CA 存储已过时或根本没有,则验证会失败。
如果服务器没有正确更新其证书,证书可能会过期,然后验证失败。同样,为了进行正确的验证,您的客户端需要一个至少与现实大致同步的时钟,否则验证可能会失败。
与跳过整个步骤的速度相比,验证还需要更多时间。有时对某些人来说,它具有奇怪的吸引力。
然而,所有与此相关的curl 和libcurl 文档都强烈建议用户不要禁用该检查。
libcurl 时间轴
curl 在 1998 年 4 月添加了对 SSL 的支持(几年前他们将其重命名为 TLS)。自 2002 年以来,curl 默认对工具和库进行证书检查。当时,我觉得自己反应有点慢,但至少我们最终确保了curl用户会默认执行此检查。
十年后,即 2012 年 10 月,发表了一篇名为《世界上最危险的代码》的论文,其中作者坚称,应用程序不使用 libcurl 验证 TLS 证书的普遍问题是因为这个接口几乎是极其糟糕的。问题显然出在 libcurl 的 API 上。
同样的“事实”稍后会重复,例如在2014 年的演示中,它说这是我们的错,因为 API(针对 PHP)看起来需要一个布尔值,但实际上并非如此。
libcurl API 用于此目的
我并不是说我们在 libcurl 中拥有最好的 API,但我可以说,很少有库可以拥有与我们接近的 API 和 ABI 稳定性。自 2006 年以来,我们就没有违反过 ABI。我们不介意将一个已经学会依赖这个和我们的世界扛在肩上。所以我们不会改变 API,尽管它本来可以做得更好一点。
CURLOPT_SSL_VERIFYPEER是一个布尔选项,用于请求针对 CA 存储进行服务器证书验证。默认情况下它设置为 TRUE,因此应用程序需要将其设置为 FALSE (0) 以禁用检查。此选项与下一个选项一起使用。
CURLOPT_SSL_VERIFYHOST是一个单独的选项,用于验证证书中嵌入的名称是否与 URL 中的名称匹配(基本上)。这个选项从来都不是布尔值,而是接受数字。 0 禁用检查,2 表示最大检查级别。默认值为 2。
因此,默认情况下,这两个选项都设置为验证,并且应用程序可以通过更改其中一个或两个选项来减少检查。
改编
在 2012 年发布那篇最危险的文章之后,基本上说我们毫无价值,但没有告诉我们这一点,也没有向我们提交问题或拉请求,我们改变了 CURLOPT_SSL_VERIFYHOST 在 7.28.1 版本中的工作方式——2012 年 12 月发布。
从那时起,我们将该选项设置为 1 是一个错误(并且它只会保持原始值不变)。在此更新之前,将 VERIFYHOST 设置为 1 是一种类似调试的模式,该模式会使 libcurl 在不匹配时输出警告,但仍允许连接通过。提供一种愚蠢的模式。
2019 年,我们进一步调整了 VERIFYHOST 处理,使值 1 和 2 执行相同的操作:验证名称。
我不知道 2012 年那篇论文的作者会如何看待这个 API 调整,但至少选项现在是两个正确的布尔值。
当最初发表那篇论文时,我认为作者的观点并不正确,但我们还是对 API 做了一些改进。我敢说禁用证书检查的问题并不是因为 libcurl API 不好。
卷曲
当然,curl 工具是一个使用 libcurl 的应用程序,它本身提供了--insecure
( -k
) 选项,该选项在使用时会关闭上述两个 libcurl 选项。也强烈建议不要在测试和分类之外实际使用。
其他层在上面
libcurl 本身被许多框架和语言使用,向各自的用户公开选项。他们甚至经常使用相同的选项名称。我们有超过 60 个记录的 libcurl 语言绑定。
例如,PHP/CURL 绑定非常流行且使用广泛,并且它使用完全相同的名称、值和行为提供和公开的选项。
禁用检查
默认启用此功能已经超过二十二年了。距离最危险的论文发表已有十二年多了。在无数关于该主题的文章之后。我交谈过的每个人都知道我们都必须验证证书。
在几乎所有情况下,您都可以以正确的方式修复失败的验证,而不是禁用检查。通常只是多做一点工作。
今天使用 libcurl 检查的状态
我于 2025 年 2 月 10 日在 GitHub 上搜索“CURLOPT_SSL_VERIFYPEER, FALSE”,它很快就向我显示了大约 140,000 个匹配的存储库。当然,并非所有这些匹配都是不好的用途,因为它们可以有条件地完成等,也可以使用其他绑定来完成,使用此搜索未捕获的不同选项名称等。或者它们可能使用固定,这也不会被这个简单的搜索词捕获。
搜索“CURLOPT_SSL_VERIFYPEER, 0”会显示 153,000 个其他匹配项。
快速浏览显示,在这些匹配项中,有很多真正的、大多数是草率的、使用代码禁用卷曲的证书。
我们可能会自欺欺人地认为,在由大团队开发的广泛使用的现代软件中,证书检查禁用的状态更好。
快速 CVE 搜索立即发现了几个与去年才发布的问题相关的安全漏洞:
- CVE-2024-32928 – Nest 生产设备发出的部分请求中禁用了 libcurl CURLOPT_SSL_VERIFYPEER 选项。
- CVE-2024-56521 – 在 6.8.0 之前的 TCPDF 中发现了一个问题。如果使用 libcurl,则 CURLOPT_SSL_VERIFYHOST 和 CURLOPT_SSL_VERIFYPEER 设置不安全。
- CVE-2024-5261 – 在受影响的 Collabora Online 版本中,在 LibreOfficeKit 中,curl 的 TLS 证书验证被禁用(CURLOPT_SSL_VERIFYPEER 为 false)。
如果我对错误赏金感兴趣,我就会知道从哪里开始。
我们做什么?
显然,这是一项永远无法完成或完成的工作——随着软件数量的增长,可能会变得更糟。我们需要不断告诉人们解决这个问题。停止鼓励别人做错事。以身作则。提供良好的文档和片段供人们复制。
我迈出了很小的一步,报告了一个针对似乎鼓励禁用的文档的错误。如果我们在看到这些问题时都提交一两个错误
当/如果您也提交错误报告时,请记住保持礼貌、友好且切中要点。解释为什么禁用检查是不好的。为什么保留支票是好的。
冲洗并重复。直到时间的尽头。
原文: https://daniel.haxx.se/blog/2025/02/11/disabling-cert-checks-we-have-not-learned-much/