diff options
author | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-07-26 03:21:52 +0000 |
---|---|---|
committer | normal <normal@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2018-07-26 03:21:52 +0000 |
commit | 9749bfbf735f8dca3361f2ea16bb97027bd1ab61 (patch) | |
tree | cca1f10d0971f7721cd06e3867ba4acaea57bc14 /lib | |
parent | 1516b85d54d23097138b384a6364ca36d00cbad4 (diff) | |
download | ruby-9749bfbf735f8dca3361f2ea16bb97027bd1ab61.tar.gz |
webrick: Support bcrypt password hashing
This adds a password_hash keyword argument to
WEBrick::HTTPAuth::Htpasswd#initialize. If set to :bcrypt, it
will create bcrypt hashes instead of crypt hashes, and will
raise an exception if the .htpasswd file uses crypt hashes.
If :bcrypt is used, then instead of calling
BasicAuth.make_passwd (which uses crypt),
WEBrick::HTTPAuth::Htpasswd#set_passwd will set the bcrypt
password directly. It isn't possible to change the
make_passwd API to accept the password hash format, as that
would break configurations who use Htpasswd#auth_type= to set
a custom auth_type.
This modifies WEBrick::HTTPAuth::BasicAuth to handle checking
both crypt and bcrypt hashes.
There are commented out requires for 'string/crypt', to handle
when String#crypt is deprecated and the undeprecated version is
moved to a gem.
There is also a commented out warning for the case when
the password_hash keyword is not specified and 'string/crypt'
cannot be required. I think the warning makes sense to nudge
users to using bcrypt.
I've updated the tests to test nil, :crypt, and :bcrypt values
for the password_hash keyword, skipping the bcrypt tests if the
bcrypt library cannot be required.
[ruby-core:88111] [Feature #14940]
From: Jeremy Evans <code@jeremyevans.net>
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64060 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r-- | lib/webrick/httpauth/basicauth.rb | 12 | ||||
-rw-r--r-- | lib/webrick/httpauth/htpasswd.rb | 37 |
2 files changed, 45 insertions, 4 deletions
diff --git a/lib/webrick/httpauth/basicauth.rb b/lib/webrick/httpauth/basicauth.rb index e23420fdfa..751885bc3e 100644 --- a/lib/webrick/httpauth/basicauth.rb +++ b/lib/webrick/httpauth/basicauth.rb @@ -24,7 +24,7 @@ module WEBrick # # config = { :Realm => 'BasicAuth example realm' } # - # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file' + # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file', password_hash: :bcrypt # htpasswd.set_passwd config[:Realm], 'username', 'password' # htpasswd.flush # @@ -81,7 +81,15 @@ module WEBrick error("%s: the user is not allowed.", userid) challenge(req, res) end - if password.crypt(encpass) != encpass + + case encpass + when /\A\$2[aby]\$/ + password_matches = BCrypt::Password.new(encpass.sub(/\A\$2[aby]\$/, '$2a$')) == password + else + password_matches = password.crypt(encpass) == encpass + end + + unless password_matches error("%s: password unmatch.", userid) challenge(req, res) end diff --git a/lib/webrick/httpauth/htpasswd.rb b/lib/webrick/httpauth/htpasswd.rb index 976eeeb13e..cff18a8012 100644 --- a/lib/webrick/httpauth/htpasswd.rb +++ b/lib/webrick/httpauth/htpasswd.rb @@ -35,11 +35,29 @@ module WEBrick ## # Open a password database at +path+ - def initialize(path) + def initialize(path, password_hash: nil) @path = path @mtime = Time.at(0) @passwd = Hash.new @auth_type = BasicAuth + @password_hash = password_hash + + case @password_hash + when nil + # begin + # require "string/crypt" + # rescue LoadError + # warn("Unable to load string/crypt, proceeding with deprecated use of String#crypt, consider using password_hash: :bcrypt") + # end + @password_hash = :crypt + when :crypt + # require "string/crypt" + when :bcrypt + require "bcrypt" + else + raise ArgumentError, "only :crypt and :bcrypt are supported for password_hash keyword argument" + end + File.open(@path,"a").close unless File.exist?(@path) reload end @@ -56,6 +74,14 @@ module WEBrick line.chomp! case line when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z! + if @password_hash == :bcrypt + raise StandardError, ".htpasswd file contains crypt password, only bcrypt passwords supported" + end + user, pass = line.split(":") + when %r!\A[^:]+:\$2[aby]\$\d{2}\$.{53}\z! + if @password_hash == :crypt + raise StandardError, ".htpasswd file contains bcrypt password, only crypt passwords supported" + end user, pass = line.split(":") when /:\$/, /:{SHA}/ raise NotImplementedError, @@ -102,7 +128,14 @@ module WEBrick # Sets a password in the database for +user+ in +realm+ to +pass+. def set_passwd(realm, user, pass) - @passwd[user] = make_passwd(realm, user, pass) + if @password_hash == :bcrypt + # Cost of 5 to match Apache default, and because the + # bcrypt default of 10 will introduce significant delays + # for every request. + @passwd[user] = BCrypt::Password.create(pass, :cost=>5) + else + @passwd[user] = make_passwd(realm, user, pass) + end end ## |