Heroku Rails Net::HTTP: OpenSSL::SSL::SSLError: SSL_connect Returned=1 errno=0 state=SSLv3 读取服务器证书 B: 证书验证失败

Dav*_*ard 5 ruby ssl ruby-on-rails heroku ruby-on-rails-3

我有一个在 Heroku 服务器上运行的 Rails 应用程序,但我无法通过 HTTPS 使用 Net::HTTP 与外部服务器进行通信。每当我尝试POST通过 HTTPS 访问外部专有 API时,我收到的错误是:

OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
Run Code Online (Sandbox Code Playgroud)

我已经做了好几个小时的谷歌搜索,以寻找我遇到的问题的答案,但无济于事。这是我的环境:

  • Heroku Dyno 运行 Cedar 14(正在运行 Cedar 10,升级到 Cedar 14 以查看它是否会影响问题 - 不高兴)
  • 导轨 3.2.14rc2
  • 红宝石 2.1.2

“修复”我试过:

  • 运行certifiedgem(实际上似乎有助于通过 Omniauth 和 Google 的 API 进行通信)
  • Monkey-patch 从验证方法列表中删除 SSLv2、SSLv3。当我在 Heroku 服务器上的控制台中尝试此操作时,它对于 GET 方法似乎工作得很好,但是在尝试运行Net::HTTP::Post.new而不是尝试运行时似乎完全忽略了调整Net::HTTP::Get.new
  • 手动覆盖证书文件位置。我已经指定它使用操作系统的授权文件(在 Cedar-14 堆栈上是最新的),但仍然没有骰子。
  • 我还尝试在 Net::HTTP 对象上手动指定要使用但ssl_version="TLSv1_2"没有运气(它甚至一直报告相同的SSLv3相关错误)

在开发中本地运行时,通信似乎工作得很好(我使用了此处建议RVM 覆盖方法),但是当我在 Heroku 服务器上尝试时,我很不走运。

更新:即使使用 RVM 更新,这似乎也无法在本地工作。

最后但并非最不重要的是,这是我正在运行的代码的抽象变体:

uri = URI.parse("https://api.mysite.com/api/v1")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == "https")
request = Net::HTTP::Post.new(uri.path, {'Content-Type' =>'application/json'})
request_body = "{\"post_body\": \"data1\"}"
response = http.request(request)
Run Code Online (Sandbox Code Playgroud)

有人对我还应该看什么有什么建议吗?

Chr*_*ald 3

Heroku 无法验证您的服务器证书是否由它识别的 CA 根有效签名。这可能是因为:

  1. 您的证书未由 CA 或中间人签名(即自签名)
  2. 你的证书是由 Heroku 不知道的 CA 签署的(不太可能)
  3. API 服务器未提供正确的中间证书来帮助 Heroku 将其连接到有效的 CA 根。(可能)

从你的 shell 中尝试一下openssl s_client -showcerts -connect your-api-host.com:443。您应该看到类似以下内容:

depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Domain Validation Secure Server CA
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL, CN = www.coffeepowered.net
verify return:1
Run Code Online (Sandbox Code Playgroud)

您特别希望确保链中的所有证书都返回verify return: 1. 如果这在您的 shell 中有效,那么您的计算机可能安装了根证书,而您的 Heroku 实例没有安装。

如果不确切知道您的 API 服务器返回什么证书,则很难明确回答这个问题,但您可能需要与 SSL 证书本身一起提供中间证书包。该中间证书包将由您的 SSL 证书签名者提供,并且可以通过SSLCertificateChainFile在 Apache 中提供,或者通过将中间证书与您的证书连接在 nginx 中提供(根据本文档)。

如果您无法更改 API 服务器的配置,那么您的“手动覆盖证书文件位置”解决方案可能非常接近正确(这与提供中间证书的服务器相同,只是客户端这样做),但您可能没有为 API 服务器的证书提供正确的证书链包。确保您向 OpenSSL 提供了正确的中间证书链,并且它应该按需要工作。