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

Popular posts from this blog

networking - Vagrant-provisioned VirtualBox VM is not reachable from Ubuntu host -

c# - ASP.NET Core - There is already an object named 'AspNetRoles' in the database -

ruby on rails - ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true -