Read more

Ruby: How to connect to a host with expired SSL certificate

Arne Hartherz
April 08, 2024Software engineer at makandra GmbH

If you need to make an HTTPS connection to a host which uses an expired certificate, do not disable certificate verifications entirely. Doing that enables e.g. man in the middle attacks.
If you accept only a single expired and known certificate, you are much less in trouble.

Setup

Illustration online protection

Rails Long Term Support

Rails LTS provides security patches for old versions of Ruby on Rails (2.3, 3.2, 4.2 and 5.2)

  • Prevents you from data breaches and liability risks
  • Upgrade at your own pace
  • Works with modern Rubies
Read more Show archive.org snapshot

All the solutions described below use a verify_callback for the request's OpenSSL::X509::Store where you can specify a lambda to adjust its verification response.
Your callback must return either true or false and OpenSSL's verification result is the first callback block argument.

In our examples, we will be connecting to expired.badssl.com which uses an expired certificate.
The certificate is also a wildcard certificate, so its Common Name property (CN) is set to *.badssl.com.

Our verify_callback will allow connecting to an expired host only when its certificate contains CN=*.badssl.com.

uri = URI.parse('https://expired.badssl.com')
expected_common_name = '*.badssl.com'

verify_callback = lambda do |preverify_ok, store_context|
  if store_context.error == OpenSSL::X509::V_ERR_CERT_HAS_EXPIRED && store_context.current_cert.subject.to_s.include?("CN=#{expected_common_name}")
    # The certiciate for that host is expired, but it's fine for us.
    true
  else
    preverify_ok 
  end
end

Using that is fairly straightforward in your favorite connection library. We've described a few below.

Net::HTTP

Ruby's Net::HTTP requires the longest setup of all:

require 'net/https'

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
http.verify_callback = verify_callback

get = Net::HTTP::Get.new(uri.request_uri)
response = http.request(get)
puts response.body

HTTP

Using the http Show archive.org snapshot gem, you can pass along the verify_callback as an option like so:

require 'http'

response = HTTP.get(uri, ssl: { verify_callback: verify_callback })
puts response.body

RestClient

The rest-client Show archive.org snapshot gem also offers an ssl_verify_callback option, but not for its top-level utility methods, like RestClient.get.
Instead, you can use a RestClient::Resource.new like so:

require 'rest-client'

resource = RestClient::Resource.new(uri.to_s, ssl_verify_callback: verify_callback)
puts resource.get
Posted by Arne Hartherz to makandra dev (2024-04-08 10:55)