为什么Ruby无法验证SSL证书?

时间:2021-04-25 23:02:28

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

这是我第一次尝试使用XMLRPC::客户端库与远程API交互,我一直收到这个错误:

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

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.

在我的周围搜索时,我发现很多人都犯了同样的错误。通常是使用自签名证书,他们只是希望它消失,所以他们做了一些肮脏的事情,比如像XMLRPC::Client打开它的http会话一样。

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:

我最初以为这只是客户不关心证书是否有效,所以我继续搜索,发现了这个宝石。它只强制对所有SSL证书进行验证,如果不能,则抛出一个硬错误。这正是我想要的。我把它包括在内,再次运行代码,现在我得到了这个:

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

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

当然!证书是不好的!但我再次检查,以确保openssl的builtin s_client是这样的:

openssl s_client -connect sub.example.com:443

and what do I get:

我得到了什么:

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

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(命令行版本)说证书很好。OpenSSL (Ruby库)不同意这种观点。我所有的浏览器都说证书很好。

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.

一些可能有用的附加细节。该证书是通配符,但对域有效。openssl s_client与Ruby代码运行的时间间隔相同。这是使用RVM安装的Ruby 1.8.7 p357。

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使用的不是宿主OS提供的CA包吗?是否有办法告诉Ruby使用特定的CA包或系统包?

2 个解决方案

#1


107  

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.

如果您只对如何使Ruby与OpenSSL s_client或您的浏览器具有相同的行为感兴趣,您可以跳到最后一节,我将在后面的内容中详细介绍。

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:

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

  • Store#add_file takes a path to a PEM/DER-encoded certificate
  • 存储#add_file接受PEM/ der编码的证书的路径。
  • Store#add_cert takes an instance of X509::Certificate
  • 存储#add_cert以X509为例::证书。
  • Store#add_path takes a path to a directory where trusted certificates can be found
  • 存储#add_path路径到一个目录,在该目录中可以找到受信任的证书。

The "Browser" Approach

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.

这与使用浏览器、Java (cacerts)或拥有内部可信证书存储的Windows形成了对比。在那里,软件预先配置了一组受信任的证书,这些证书被认为是软件供应商的“好”。一般来说,这并不是一个坏主意,但是如果您实际查看这些集,您将很快注意到证书太多了。个人无法真正判断是否应该盲目地信任这些证书。

The Ruby Approach

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应用程序的需求与浏览器的需求有很大的不同。浏览器必须能够让您导航到任何带有TLS证书并通过https提供服务的“合法”web站点。但是在典型的Ruby应用程序中,您只需要处理一些使用TLS的服务,或者需要证书验证。

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.

Ruby方法还有一个好处——尽管它需要更多的手工工作,但最终您将得到一个手工定制的解决方案,该解决方案完全信任它在给定应用程序上下文中应该信任的证书。这很繁琐,但是安全性要高得多,因为您暴露的攻击面要少得多。以最近发生的事情为例:如果你从来没有把DigiNotar或任何其他被破坏的根包含在你的信任集中,那么这种破坏就不可能影响你。

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扩展将无法验证任何对等证书。为了使事情正常工作,您必须手动设置配置。

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:::SSL::VERIFY_PEER = OpenSSL: SSL::VERIFY_NONE。请不要这样做。我们甚至还开玩笑说,如果我们遇到这种情况,可以添加一些代码,让你的应用程序随机崩溃:)

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扩展的行为与OpenSSL CLI命令(如s_client)完全相同。

Why s_client can verify the certificate

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.

OpenSSL对浏览器和Windows使用了类似的方法。一个典型的安装将会在您的硬盘上放置一个受信任的证书包(类似于/etc/ssl/certs/ca-bundle.crt),这将作为受信任证书的缺省集合。这就是s_client在需要验证对等证书时的位置,这就是您的实验成功的原因。

Making Ruby act like s_client

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验证证书时得到同样的安慰,那么您可以通过调用OpenSSL:::X509::Store#set_default_paths来告诉它,如果在您的系统上有可信证书的话,可以使用OpenSSL包。更多信息可以在这里找到。要使用XMLRPC:::Client,只需确保在它使用的X509::Store上调用set_default_paths。

#2


0  

If you have a ca-certificates file, just do this:

如果您有一个ca证书文件,请执行以下操作:

http.ca_file = <YOUR CA-CERT FILE PATH>
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.verify_depth = 5

#1


107  

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.

如果您只对如何使Ruby与OpenSSL s_client或您的浏览器具有相同的行为感兴趣,您可以跳到最后一节,我将在后面的内容中详细介绍。

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:

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

  • Store#add_file takes a path to a PEM/DER-encoded certificate
  • 存储#add_file接受PEM/ der编码的证书的路径。
  • Store#add_cert takes an instance of X509::Certificate
  • 存储#add_cert以X509为例::证书。
  • Store#add_path takes a path to a directory where trusted certificates can be found
  • 存储#add_path路径到一个目录,在该目录中可以找到受信任的证书。

The "Browser" Approach

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.

这与使用浏览器、Java (cacerts)或拥有内部可信证书存储的Windows形成了对比。在那里,软件预先配置了一组受信任的证书,这些证书被认为是软件供应商的“好”。一般来说,这并不是一个坏主意,但是如果您实际查看这些集,您将很快注意到证书太多了。个人无法真正判断是否应该盲目地信任这些证书。

The Ruby Approach

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应用程序的需求与浏览器的需求有很大的不同。浏览器必须能够让您导航到任何带有TLS证书并通过https提供服务的“合法”web站点。但是在典型的Ruby应用程序中,您只需要处理一些使用TLS的服务,或者需要证书验证。

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.

Ruby方法还有一个好处——尽管它需要更多的手工工作,但最终您将得到一个手工定制的解决方案,该解决方案完全信任它在给定应用程序上下文中应该信任的证书。这很繁琐,但是安全性要高得多,因为您暴露的攻击面要少得多。以最近发生的事情为例:如果你从来没有把DigiNotar或任何其他被破坏的根包含在你的信任集中,那么这种破坏就不可能影响你。

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扩展将无法验证任何对等证书。为了使事情正常工作,您必须手动设置配置。

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:::SSL::VERIFY_PEER = OpenSSL: SSL::VERIFY_NONE。请不要这样做。我们甚至还开玩笑说,如果我们遇到这种情况,可以添加一些代码,让你的应用程序随机崩溃:)

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扩展的行为与OpenSSL CLI命令(如s_client)完全相同。

Why s_client can verify the certificate

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.

OpenSSL对浏览器和Windows使用了类似的方法。一个典型的安装将会在您的硬盘上放置一个受信任的证书包(类似于/etc/ssl/certs/ca-bundle.crt),这将作为受信任证书的缺省集合。这就是s_client在需要验证对等证书时的位置,这就是您的实验成功的原因。

Making Ruby act like s_client

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验证证书时得到同样的安慰,那么您可以通过调用OpenSSL:::X509::Store#set_default_paths来告诉它,如果在您的系统上有可信证书的话,可以使用OpenSSL包。更多信息可以在这里找到。要使用XMLRPC:::Client,只需确保在它使用的X509::Store上调用set_default_paths。

#2


0  

If you have a ca-certificates file, just do this:

如果您有一个ca证书文件,请执行以下操作:

http.ca_file = <YOUR CA-CERT FILE PATH>
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.verify_depth = 5