ssl - How to harden rails+webrick+https with insecure ciphers removed on Ruby 2.2 -
updated: @ first, test code didn't adequately show ruby 2.4 sees :sslciphers option whereas ruby 2.2 not. have edited example code below make clear.
i have small rails 3 application on ruby 2.2 , webrick handles small loads , therefore not need complexity of "real" web server. has been patched support https connections secure logins, default, accepts many old weak ciphers, want prohibit. while upgrading ruby 2.4 offers new hardening options achieve this, can't make switch newer ruby. therefore, have attempted monkey-patch ruby 2.2's webrick/ssl.rb add couple of these options, :sslversion , :sslciphers. partially successful: while :sslversion appears work, :sslciphers not.
first, following example shows successful hardening on ruby 2.4, based on answer here https://stackoverflow.com/a/23283909/6588873 incorporating of advice found here: https://gist.github.com/tam7t/86eb4793e8ecf3f55037 except we'll over-aggressive , exclude aes128 show :sslciphers option works (as version of openssl in ruby 2.4 disables sha1 ciphers default anyway).
#!/usr/bin/env ruby.exe # script/rails: require 'rails/commands/server' require 'rack' require 'webrick' require 'webrick/https' module rails class server < ::rack::server ssl_enabled=true def default_options # don't use sslv3 no_ssl_3 = openssl::ssl::op_no_sslv3 # don't use sslv2 no_ssl_2 = openssl::ssl::op_no_sslv2 # don't use compression (crime cve-2012-4929) no_ssl_compression = openssl::ssl::op_no_compression ssl_options = no_ssl_2 + no_ssl_3 + no_ssl_compression super.merge({ :port => 3002, :environment => (env['rails_env'] || "development").dup, :daemonize => false, :debugger => false, :config => file.expand_path("config.ru"), :sslenable => true, :sslverifyclient => openssl::ssl::verify_none, :sslprivatekey => openssl::pkey::rsa.new( file.open(file.expand_path("../cert/test.cert.key",__file__)).read), :sslcertificate => openssl::x509::certificate.new( file.open(file.expand_path("../cert/test.cert.crt",__file__)).read), :sslcertname => [["cn", webrick::utils::getservername]], :ssloptions => ssl_options, :sslciphers => 'tlsv1.2:!anull:!enull:!aes128', :sslversion => :tlsv1_2, }) end end end app_path = file.expand_path('../../config/application', __file__) require file.expand_path('../../config/boot', __file__) require 'rails/commands'
and here's excerpt sslscan output above patch applied, showing short list of supported ciphers insecure ciphers , obsolete tls versions dropped (though dropped anyway default, hence !aes128 proof of principle showing option works).
supported server cipher(s): preferred tlsv1.2 256 bits ecdhe-rsa-aes256-gcm-sha384 curve p-256 dhe 256 accepted tlsv1.2 256 bits ecdhe-rsa-aes256-sha384 curve p-256 dhe 256 accepted tlsv1.2 256 bits dhe-rsa-aes256-gcm-sha384 dhe 1024 bits accepted tlsv1.2 256 bits dhe-rsa-aes256-sha256 dhe 1024 bits accepted tlsv1.2 256 bits aes256-gcm-sha384 accepted tlsv1.2 256 bits aes256-sha256
so next, changed :sslciphers actual string want, since i'm fine aes128. want exclude old sha1 ciphers older openssl doesn't exclude default:
:sslciphers => 'tlsv1.2:!anull:!enull!sha',
and prepared backport, inserted after rails::server patch shown above. following lifted directly ruby 2.2's webrick/ssl.rb, 2 new options ruby 2.4 added , passed through ruby-openssl via webrick::genericserver#setup_ssl_context():
if ruby_version < '2.4' module webrick module config svrsoft = general[:serversoftware] osslv = ::openssl::openssl_version.split[1] ssl = { :serversoftware => "#{svrsoft} openssl/#{osslv}", :sslenable => false, :sslcertificate => nil, :sslprivatekey => nil, :sslclientca => nil, :sslextrachaincert => nil, :sslcacertificatefile => nil, :sslcacertificatepath => nil, :sslcertificatestore => nil, :ssltmpdhcallback => nil, :sslverifyclient => ::openssl::ssl::verify_none, :sslverifydepth => nil, :sslverifycallback => nil, # custom verification :ssltimeout => nil, :ssloptions => nil, :sslciphers => nil, :sslversion => nil, :sslstartimmediately => true, # must specify if use auto generated certificate. :sslcertname => nil, :sslcertcomment => "generated ruby/openssl" } general.update(ssl) end class genericserver def setup_ssl_context(config) # :nodoc: unless config[:sslcertificate] cn = config[:sslcertname] comment = config[:sslcertcomment] cert, key = utils::create_self_signed_cert(1024, cn, comment) config[:sslcertificate] = cert config[:sslprivatekey] = key end ctx = openssl::ssl::sslcontext.new ctx.key = config[:sslprivatekey] ctx.cert = config[:sslcertificate] ctx.client_ca = config[:sslclientca] ctx.extra_chain_cert = config[:sslextrachaincert] ctx.ca_file = config[:sslcacertificatefile] ctx.ca_path = config[:sslcacertificatepath] ctx.cert_store = config[:sslcertificatestore] ctx.tmp_dh_callback = config[:ssltmpdhcallback] ctx.verify_mode = config[:sslverifyclient] ctx.verify_depth = config[:sslverifydepth] ctx.verify_callback = config[:sslverifycallback] ctx.timeout = config[:ssltimeout] ctx.options = config[:ssloptions] ctx.ciphers = config[:sslciphers] ctx.ssl_version = config[:sslversion] ctx end end end end
when started, see following redefinition warnings. first, expected. second i'm not sure of, think ok:
script/rails:47: warning: initialized constant webrick::config::ssl c:/ruby226/lib/ruby/2.2.0/webrick/ssl.rb:62: warning: previous definition of ssl here
now, while sslscan indicates sslversion being obeyed, (i.e. no tls 1.0 or 1.1 accepted,) sslciphers option doesn't appear doing @ all, i.e. sha ciphers still accepted:
supported server cipher(s): preferred tlsv1.2 256 bits dhe-rsa-aes256-gcm-sha384 dhe 1024 bits accepted tlsv1.2 256 bits dhe-rsa-aes256-sha256 dhe 1024 bits accepted tlsv1.2 256 bits dhe-rsa-aes256-sha dhe 1024 bits accepted tlsv1.2 256 bits dhe-rsa-camellia256-sha dhe 1024 bits accepted tlsv1.2 256 bits aes256-gcm-sha384 accepted tlsv1.2 256 bits aes256-sha256 accepted tlsv1.2 256 bits aes256-sha accepted tlsv1.2 256 bits camellia256-sha accepted tlsv1.2 128 bits dhe-rsa-aes128-gcm-sha256 dhe 1024 bits accepted tlsv1.2 128 bits dhe-rsa-aes128-sha256 dhe 1024 bits accepted tlsv1.2 128 bits dhe-rsa-aes128-sha dhe 1024 bits accepted tlsv1.2 128 bits dhe-rsa-seed-sha dhe 1024 bits accepted tlsv1.2 128 bits dhe-rsa-camellia128-sha dhe 1024 bits accepted tlsv1.2 128 bits aes128-gcm-sha256 accepted tlsv1.2 128 bits aes128-sha256 accepted tlsv1.2 128 bits aes128-sha accepted tlsv1.2 128 bits seed-sha accepted tlsv1.2 128 bits camellia128-sha
whereas expected:
supported server cipher(s): preferred tlsv1.2 256 bits dhe-rsa-aes256-gcm-sha384 dhe 1024 bits accepted tlsv1.2 256 bits dhe-rsa-aes256-sha256 dhe 1024 bits accepted tlsv1.2 256 bits aes256-gcm-sha384 accepted tlsv1.2 256 bits aes256-sha256 accepted tlsv1.2 128 bits dhe-rsa-aes128-gcm-sha256 dhe 1024 bits accepted tlsv1.2 128 bits dhe-rsa-aes128-sha256 dhe 1024 bits accepted tlsv1.2 128 bits aes128-gcm-sha256 accepted tlsv1.2 128 bits aes128-sha256
in fact, doesn't matter change :sslciphers to, valid or invalid value. clearly, updating ruby not enough.
where have gone wrong backport? there else can try achieve same result? seem close solution here, working within constraints of our ruby version , webrick, difficult me move away from.
Comments
Post a Comment