为什么 Ruby 无法验证 SSL 证书? [英] Why is Ruby unable to verify an SSL certificate?

查看:44
本文介绍了为什么 Ruby 无法验证 SSL 证书?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我第一次尝试使用 XMLRPC::Client 库与远程 API 交互,但我一直收到此错误:

This is my first time trying to use the XMLRPC::Client library to interact with a remote API and I keep receiving this error:

warning: peer certificate won't be verified in this SSL session

四处搜索,我发现很多人都遇到了这个错误.通常它带有自签名证书,他们只是想让它消失,所以他们做了一些肮脏的事情,比如 XMLRPC::Client 打开它的 http 会话的方式.

Searching around I've found loads of people that have gotten that error. Usually it's with self-signed certificates and they just want it to go away, so they do something dirty like monkey patch the way XMLRPC::Client is opening it's http session.

我首先认为这只是客户不关心证书是否有效,所以我继续搜索并遇到了 这颗宝石.它只是强制验证所有 SSL 证书,如果它也无法验证,则会抛出一个硬错误.这正是我想要的.我包含它,再次运行代码,现在我得到了这个:

I first assumed this was simply the client not caring whether the certificate was valid or not, so I continued my search and came across this gem. It simply forces verification of all SSL certificates and throws a hard error if it's not able too. This was exactly what I wanted. I included it, ran the code again and now I'm getting this:

OpenSSL:SSL::SSLError:
  SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B:
  certificate verify failed

当然!证书不好!但我仔细检查只是为了确保使用 openssl 的内置 s_client 像这样:

Of course! The certificate is bad! But I double check just to make sure with openssl's builtin s_client like so:

openssl s_client -connect sub.example.com:443

我能得到什么:

CONNECTED(00000003)
---
Certificate chain
<snip>
Verify return code: 0 (ok)

现在我们来回答我的问题.OpenSSL(命令行版本)表示证书很好.OpenSSL(Ruby 库)不同意.我所有的网络浏览器都说证书很好.

So now we get to my question. OpenSSL (the command line version) says the certificate is good. OpenSSL (the Ruby library) disagrees. All of my web browsers say the certificate is good.

一些可能有用的额外细节.证书是通配符,但对域有效.openssl s_client 与 Ruby 代码在同一台机器上运行几秒钟.这是与 RVM 一起安装的 Ruby 1.8.7 p357.

A few additional details that might be of use. The certificate is a wildcard but is valid for the domain. The openssl s_client was run on the same machine seconds apart from the Ruby code. This is Ruby 1.8.7 p357 which is installed with RVM.

Ruby 是否使用主机操作系统提供的 CA 包以外的其他东西?有没有办法告诉 Ruby 使用特定的 CA 包或系统包?

Does Ruby use something other than the CA bundle provided by the host OS? Is there a way to tell Ruby to use a specific CA bundle or the system one?

推荐答案

如果您只对如何使 Ruby 的行为与 OpenSSL s_client 或您的浏览器相同,您可以跳到在最后一部分,我将在以下内容中介绍细则.

If you are only interested in how to make Ruby behave the same way as OpenSSL s_client or your browser does, you may skip to the very last section, I'll cover the fine print in what is following.

默认情况下,用于建立连接的 OpenSSL::X509::Store 根本不使用任何受信任的证书.根据您对应用程序域的了解,您通常会向 X509::Store 的实例提供与您的应用程序相关的可信证书.对此有多种选择:

By default, the OpenSSL::X509::Store used for making the connection doesn't use any trusted certificates at all. Based on your knowledge of the application domain, you would typically feed an instance of X509::Store with the trusted certificate(s) that are relevant for your application. There are several options for this:

  • Store#add_file 采用 PEM/DER 编码证书的路径
  • Store#add_cert 采用 X509::Certificate 的实例
  • Store#add_path 获取可找到受信任证书的目录的路径

这与浏览器、Java (cacerts) 或具有自己内部可信证书存储的 Windows 所采用的方法形成对比.在那里,软件预先配备了一组受软件供应商认为良好"的可信证书.一般来说,这不是一个坏主意,但如果你真的研究这些集合,那么你很快就会注意到证书太多了.个人无法真正判断是否应该盲目信任所有这些证书.

This is in contrast to the approach browsers, Java (cacerts), or Windows with its own internal store of trusted certificates, take. There the software is pre-equipped with a set of trusted certificates that is considered to be "good" in the opinion of the software vendor. Generally this is not a bad idea, but if you actually look into these sets, then you will soon notice that there are just too many certificates. An individual can't really tell whether all of these certificates should be trusted blindly or not.

另一方面,您的典型 Rub​​y 应用程序的需求与浏览器的需求大不相同.浏览器必须能够让您导航到任何带有 TLS 证书并通过 https 提供服务的合法"网站.但在典型的 Ruby 应用程序中,您只需处理少数使用 TLS 或需要证书验证的服务.

The requirements of your typical Ruby application on the other hand are a lot different than that of a browser. A browser must be be able to let you navigate to any "legitimate" web site that comes with a TLS certificate and is served over https. But in a typical Ruby application you will only have to deal with a few services that use TLS or would otherwise require certificate validation.

Ruby 方法有一个好处——虽然它需要更多的手动工作,但您最终会得到一个手工定制的解决方案,它完全信任它在给定应用程序上下文中应该信任的证书.这很乏味,但这种方式的安全性要高得多,因为您暴露的攻击面要少得多.以最近发生的事件为例:如果您从未将 DigiNotar 或任何其他受损的根包含在您的信任集中,那么此类违规行为就不会影响您.

And there is the benefit of the Ruby approach - although it requires more manual work, you will end up with a hand-tailored solution that exactly trusts the certificates it should trust in your given application context. This is tedious, but security is much higher this way because you expose a lot less attack surface. Take recent events: if you never had to include DigiNotar or any other compromised root in your trust set, then there's no way such breaches can affect you.

然而,正如您已经注意到的,这样做的缺点是,默认情况下,如果您不主动添加可信证书,OpenSSL 扩展将无法验证任何对等证书根本.为了使事情正常工作,您必须手动设置配置.

The downside of this, however, as you have already noticed, is that by default, if you don't actively add trusted certificates, the OpenSSL extension will not be able to validate any peer certificate at all. In order to make things work, you have to set up the configuration manually.

这种不便导致了许多可疑的措施来规避它,最糟糕的是全局设置OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE.请不要这样做.我们甚至开玩笑说,如果我们遇到这种黑客攻击,添加的代码会让您的应用程序随机崩溃:)

This inconvenience has led to a lot of dubious measures to circumvent it, the worst of all being to globally set OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE. Please don't do this. We have even made jokes about adding code that lets your application crash randomly if we encounter that hack :)

如果手动信任设置看起来太复杂,我现在将提供一个简单的替代方案,使 OpenSSL 扩展的行为与 OpenSSL CLI 命令(如 s_client)完全相同.

If manual trust setup seems too complicated, I'll offer an easy alternative now that makes the OpenSSL extension behave exactly the same as OpenSSL CLI commands like s_client.

OpenSSL 使用与浏览器和 Windows 类似的方法.典型的安装会将一组受信任的证书放在硬盘上的某个位置(类似于 /etc/ssl/certs/ca-bundle.crt),这将用作默认的一组受信任证书.这就是 s_client 在需要验证对等证书时查​​看的位置,这就是您的实验成功的原因.

OpenSSL uses a similar approach to browsers and Windows. A typical installation will put a bundle of trusted certificates somewhere on your hard disk (something like /etc/ssl/certs/ca-bundle.crt) and this will serve as the default set of trusted certificates. That's where s_client looks when it needs to verify peer certificates and that's why your experiment succeeded.

如果您仍然希望在使用 Ruby 验证证书时获得同样的舒适感,您可以通过调用 OpenSSL::X509::Store# 告诉它使用系统上可用的 OpenSSL 可信证书包set_default_paths.其他信息可以在此处找到.要将其与 XMLRPC::Client 一起使用,只需确保在它使用的 X509::Store 上调用 set_default_paths.

If you'd still like to have the same comfort when validating certificates with Ruby, you can tell it to use the OpenSSL bundle of trusted certificates if available on your system by calling OpenSSL::X509::Store#set_default_paths. Additional information can be found here. To use this with XMLRPC::Client, simply ensure that set_default_paths gets called on the X509::Store it uses.

这篇关于为什么 Ruby 无法验证 SSL 证书?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆