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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9199660/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:45:29  来源:igfitidea点击:

Why is Ruby unable to verify an SSL certificate?

rubyapiopensslcertificate

提问by Sam Stelfox

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::Client 库与远程 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:

我首先假设这只是客户不关心证书是否有效,所以我继续我的搜索并遇到了这个 gem。它只是强制验证所有 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 的内置 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 是否使用主机操作系统提供的 CA 包以外的其他东西?有没有办法告诉 Ruby 使用特定的 CA 包或系统包?

回答by emboss

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

如果您只对如何使 Ruby 的行为与 OpenSSLs_client或您的浏览器相同,您可以跳到最后一部分,我将在接下来的内容中详细说明。

By default, the OpenSSL::X509::Storeused 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::Storewith the trusted certificate(s) that are relevant for your application. There are several options for this:

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

  • Store#add_file takes a path to a PEM/DER-encoded certificate
  • Store#add_cert takes an instance of X509::Certificate
  • Store#add_path takes a path to a directory where trusted certificates can be found
  • Store#add_file 采用 PEM/DER 编码证书的路径
  • Store#add_cert 采用 X509::Certificate 的实例
  • Store#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 allof these certificates should be trusted blindly or not.

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

The Ruby Approach

Ruby 方法

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.

另一方面,典型 Rub​​y 应用程序的需求与浏览器的需求大不相同。浏览器必须能够让您导航到任何带有 TLS 证书并通过 https 提供服务的“合法”网站。但在典型的 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 anypeer 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. 请不要这样做。我们甚至开玩笑说,如果我们遇到这种 hack,添加代码会让您的应用程序随机崩溃:)

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

为什么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_clientlooks 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

使 Ruby 表现得像 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_pathsgets called on the X509::Storeit uses.

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

回答by Guihen

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

如果您有 ca-certificates 文件,请执行以下操作:

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