Ruby SSL错误- SSLv3警告未预期的消息

5

我正在尝试在Ruby脚本中连接服务器https://www.xpiron.com/schedule。但是,当我尝试连接时:

require 'open-uri'
doc = open('https://www.xpiron.com/schedule')

我收到了以下错误信息:
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A:  sslv3 alert unexpected message         
    from /usr/local/lib/ruby/1.9.1/net/http.rb:678:in `connect'
    from /usr/local/lib/ruby/1.9.1/net/http.rb:678:in `block in connect'
    from /usr/local/lib/ruby/1.9.1/timeout.rb:44:in `timeout'
    from /usr/local/lib/ruby/1.9.1/timeout.rb:87:in `timeout'
    from /usr/local/lib/ruby/1.9.1/net/http.rb:678:in `connect'
    from /usr/local/lib/ruby/1.9.1/net/http.rb:637:in `do_start'
    from /usr/local/lib/ruby/1.9.1/net/http.rb:626:in `start'
    from /usr/local/lib/ruby/1.9.1/net/http.rb:1168:in `request'
    from /usr/local/lib/ruby/1.9.1/net/http.rb:888:in `get'
    from (irb):32
    from /usr/local/bin/irb:12:in `<main>'

我正在运行Ruby 1.9.2p180。它在其他一些机器上可以工作,所以可能是OpenSSL或Ruby的配置问题。我尝试重新安装所有SSL库并从头开始重建Ruby,但似乎没有什么作用。有人遇到过这个问题吗?
更新
在不工作的机器上,openssl版本为0.9.8o 01 Jun 2010
在工作的机器上,它是0.9.8k 25 Mar 2009
因此,更新的版本似乎出现了故障。
此外,如果我使用不同的HTTP客户端(基于libcurl的Patron),它可以工作:
require 'patron'

sess = Patron::Session.new
sess.timeout = 5
url = 'https://www.xpiron.com/schedule'
resp = sess.get(url)
puts "#{resp.body}"

看起来这似乎是Ruby的OpenSSL绑定出了问题。


ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION' 在无法工作的机器上会输出什么?在能够运行的机器上又会输出什么呢? - emboss
工作中:OpenSSL 0.9.8k 25 Mar 2009不工作:OpenSSL 0.9.8o 01 Jun 2010看起来较新版本中有些不同。 - marketer
5个回答

9

为了回答我的问题。

问题似乎出在Ruby如何协商SSL连接上。 Xpiron的TLS机制存在错误,它会抛出错误而不是重试其他SSL版本。

如果强制使用SSL版本3.0,则可以解决问题:

require 'net/http'
url = URI.parse('https://www.xpiron.com/schedule')
req = Net::HTTP::Get.new(url.path)
sock = Net::HTTP.new(url.host, 443)
sock.use_ssl = true
sock.ssl_version="SSLv3"
sock.start do |http|
    response = http.request(req)
end

我还在Ruby的错误跟踪器上创建了一个问题。


1
请告诉我们您在哪个文件中进行了这些修改,如果您正在使用RoR。 - Aleksandrus

2

我认为是因为https URL的缘故。虽然有一种非常不安全的方法可以绕过这个问题,但是请自行谷歌搜索风险自负。我更愿意向您展示使用Net::HTTP进行安全处理的方法:

require 'net/http'

url = URI.parse('https://www.xpiron.com/schedule')
req = Net::HTTP::Get.new(url.path)
sock = Net::HTTP.new(url.host, 443)
sock.use_ssl = true
store = OpenSSL::X509::Store.new
store.add_cert OpenSSL::X509::Certificate.new(File.new('addtrust_ca.pem'))
store.add_cert OpenSSL::X509::Certificate.new(File.new('utn.pem'))
store.add_cert OpenSSL::X509::Certificate.new(File.new('user_first_ca.pem'))
store.add_cert OpenSSL::X509::Certificate.new(File.new('xpiron.pem'))
sock.cert_store = store
sock.start do |http|
  response = http.request(req)
end

您可以通过在浏览器(例如Firefox)中输入您的URL,然后单击URL左侧的图标/更多信息/查看证书/详细信息来获取证书文件。然后单击链中的每个证书并将其导出为PEM文件,名称如上所述。将这些文件放在引用它们的脚本所在的同一目录中。那应该就可以了。但我注意到登录该页面需要一个cookie,因此可能需要适当修改您的请求以进行登录。

非常感谢您的回复。我尝试了所有方法,但错误仍然存在。我认为问题出在xpiron SSL服务器配置不正确。Chrome警告指出“必须使用SSL 3.0重试连接。这通常意味着服务器正在使用非常旧的软件,并可能存在其他安全问题”。 - marketer

1

在经过一整天的编程后,这个终于对我起作用了:

url = URI.parse("https://full-url?test=1&foo=bar")
response = Net::HTTP.start(url.host, use_ssl: true, ssl_version: 'SSLv3', verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
  http.get url.request_uri
end

0
请注意,截至目前为止,rest-client gem 没有设置这个的方法。有一个未解决的 pull request,但不幸的是,这个 gem 似乎没有得到维护。

拉取请求的新URL:https://github.com/rest-client/rest-client/pull/123 - hannes_l

0

它可以是任何东西

require 'openssl'
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接