SSL and Ruby Thursday January 23, 2014
Update: Jeff Hodges has corrected a statement in this post about the most recent OpenSSL providing secure defaults in Ruby. The error was my fault, I misread part of the email thread in question. A follow-up post is in progress, but I’ve corrected this post in the meantime.
The ruby-core position is that they do not want Ruby to be responsible for the security of users. I and others believe that Ruby already is.— Jeff Hodges (@jmhodges) January 17, 2014
Recently Jeff Hodges publicly disclosed the contents of a thread on the ruby-security mailing list. In this thread, Mr. Hodges pointed out that the lack of secure defaults means that users with older versions of OpenSSL will be susceptible to certain kinds of attacks. This generated some discussion in Ruby circles about the security responsibilities of Ruby’s volunteer maintainers.
Yes, intentionally weakened rand # generators, suspect curves, & fiber taps are bad, but we're our own worse enemy: https://t.co/4y0tqeGyHz— Kenn White (@kennwhite) January 21, 2014
At the same time, it’s been pointed out by Kenn White that open-source Ruby software already has a pretty wide-spread issue with not validating the server SSL certificate against a root CA, allowing easy man-in-the-middle attacks. In this context, better defaults are not much help, as verification is explicitly by-passed.
It’s probably important to you that your software uses SSL in the most effective way possible, so let’s see how to avoid the above issues in your own Ruby programs and environment.
Using the latest OpenSSL version
Unfortunately, at the time of this writing, updating the version of OpenSSL used by Ruby will not necessarily provide the most secure defaults. Hopefully this will be fixed in Ruby itself.
To make sure your Ruby installation is using the latest OpenSSL, you need to know which version of OpenSSL your Ruby is using, and how to update it.
First, we’ll invoke
ruby on the command line to see which version of OpenSSL
it was built with, and whether it matches the latest version listed on the
OpenSSL web site:
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION'
Helpfully, this command reports both the version and release date of the OpenSSL library used by your Ruby installation. If it’s out of date, you’ll need to update OpenSSL and then rebuild Ruby.
brew update brew upgrade openssl rbenv install -f `rbenv version`
On my machine, this process ensured that my Ruby installation utilized the latest OpenSSL, which is 1.0.1f at the time of this writing.
Verifying your SSL certificate
In SSL, trust is every bit as important as secrecy. With no way to verify identity, you have no way to verify that someone else isn’t just reading your data and passing it on. This is the reason we have root certs to begin with: They’re the root of the trust network that verifies the owner of the domain is who they say they are.
In order to verify the SSL peer with Ruby’s built-in OpenSSL and Net::HTTP, you need to specify the correct mode and cert storage location. Here is a simple example of a wrapper method that creates a Net::HTTP connection using peer verification:
require 'net/http' require 'openssl' def verified_peer_connect hostname Net::HTTP.new(hostname, 443).tap do |conn| conn.use_ssl = true conn.verify_mode = OpenSSL::SSL::VERIFY_PEER conn.cert_store = OpenSSL::X509::Store.new conn.cert_store.set_default_paths end end # Example: fetch the Google home page conn = verified_peer_connect('www.google.com') conn.get('/')
Of course, SSL will work without these settings, as those scores of open source examples can attest. The point is to more fully utilize the capabilities of SSL, including peer verification.
Ruby and Rails have been in the spotlight for security issues in the recent past, so it’s a good idea these days to be especially vigilant about security updates and best practices. Regardless of any disagreement surrounding disclosure or default ciphers, these issues can be avoided entirely with just a little bit of up-front effort.