8

I'm trying to get Ruby's Net::HTTP implementation to work with SNI.

Both mail.google.com and gmail.com live on the same IP address, so when connecting via SSL, the Google server needs to know which certificate to use. By default, it returns the mail.google.com certificate, which is a problem if you're trying to implement WebFinger.

WebFinger requires you to retrieve https://gmail.com/.well-known/host-meta to get the LRDD information, however, for security reasons, it's critical to verify the SSL certificate information.

Since Google serves up the default mail.google.com certificate in this case, the SSL post_connection_check fails. The correct solution here would be to enable Server Name Indication for Net::HTTP, but it's not clear to me how to get that working with the Ruby bindings for OpenSSL. Anyone else have an idea?

You should be able to see the problem by running:

require 'open-uri'
open('https://gmail.com/.well-known/host-meta') { |f| f.read }

I've also created a gist that exhibits the problem using an earlier version of curl and OpenSSL:

https://gist.github.com/7936ef38787092a22897

jww
  • 97,681
  • 90
  • 411
  • 885
Bob Aman
  • 32,839
  • 9
  • 71
  • 95
  • I'm all but convinced, after working on this all day long and some of yesterday, that this is an exercise in futility. I'm pretty sure that even if I were able to get SNI working, I'd have to use monkey patches to do it, making for a difficult deployment scenario. – Bob Aman Jan 14 '11 at 00:42
  • Are there any other Ruby-compatible options for dealing with this type of issue? For instance, does Net::HTTPS support `subjectAltName`? – Bob Aman Jan 14 '11 at 00:44
  • Another proposed option would be to write my own implementation of `post_connection_check` that checked hostnames against a whitelist of common name mappings. If we did this, are there any security implications to consider? – Bob Aman Jan 14 '11 at 01:05

2 Answers2

8

For SNI support, you need a newer OpenSSL release (0.9.8f with --enable-tlsext or 0.9.8j or later) and call OpenSSL::SSL::SSLSocket#hostname = 'hostname' before SSLSocket#connect. Net::HTTPS does not support SNI yet, and open-uri doesn't.

Checking out httpclient development repository should support SNI.

Let me know if you need released gem real soon now...

Bob Aman
  • 32,839
  • 9
  • 71
  • 95
NaHi
  • 246
  • 2
  • 5
  • Awesome, I'll check this out. I'm using `httpadapter`, so I should be able to make those calls. – Bob Aman Jan 14 '11 at 06:48
  • It appears that `OpenSSL::SSL::SSLSocket#hostname` is not defined? – Bob Aman Jan 14 '11 at 18:55
  • It looks to me like this is actually only supported in 1.9.2? – Bob Aman Jan 14 '11 at 19:18
  • Sorry, I was not aware of that. It's from 1.9, so 1.8.7 or earlier does not support SSLSocket#hostname=. In development version of 1.8, ruby_1_8 branch on Ruby's svn repository supports this feature but we're not sure when it's released as 1.8.8... – NaHi Jan 15 '11 at 05:42
  • Even then, that's not really a solution because it won't be widely deployed for awhile. – Bob Aman Jan 15 '11 at 07:53
  • 2
    I found that patron, typhoeus and eventmachine supports SNI. Try samples at https://github.com/nahi/odrk-http-client/tree/master/sample – NaHi Mar 02 '11 at 12:16
  • I'm migrating to Faraday soonish, and hopefully I can just document the SNI issue and let people know they can't use net/http on older versions of Ruby. I wonder if I can programmatically detect the use of an HTTPS client that doesn't support SNI... – Bob Aman Nov 23 '11 at 06:36
6

Ruby 2.0 will address the TLS SNI (Server Name Indication) issue:

from net/http..

#        ...
#           s.session = @ssl_session if @ssl_session
#           # Server Name Indication (SNI) RFC 3546
#           s.hostname = @address if s.respond_to? :hostname=
#           Timeout.timeout(@open_timeout, Net::OpenTimeout) { s.connect }
#           if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
#             s.post_connection_check(@address)
#           end
#           ...

To make this work in 1.9.2 (or higher ) apply similar patch to net/http

#         ...
# BEGIN:  SNI PATCH http://bugs.ruby-lang.org/issues/4351
#          s.hostname = @address if s.respond_to? :hostname=
# END:   SNI PATCH http://bugs.ruby-lang.org/issues/4351
#          timeout(@open_timeout) { s.connect }
#          if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
#            s.post_connection_check(@address)
#          end
#        ...

see also: http://bugs.ruby-lang.org/issues/4351 http://en.wikipedia.org/wiki/Server_Name_Indication