From 937ec342010894f7a6239883b10f6a107ff82c9f Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Sat, 31 Oct 2020 10:51:05 -0600 Subject: caddyauth: Prevent user enumeration by timing Always follow the code path of hashing and comparing a plaintext password even if the account is not found by the given username; this ensures that similar CPU cycles are spent for both valid and invalid usernames. Thanks to @tylerlm for helping and looking into this! --- modules/caddyhttp/caddyauth/hashes.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'modules/caddyhttp/caddyauth/hashes.go') diff --git a/modules/caddyhttp/caddyauth/hashes.go b/modules/caddyhttp/caddyauth/hashes.go index 5a3173e..63bfe1b 100644 --- a/modules/caddyhttp/caddyauth/hashes.go +++ b/modules/caddyhttp/caddyauth/hashes.go @@ -50,6 +50,11 @@ func (BcryptHash) Compare(hashed, plaintext, _ []byte) (bool, error) { return true, nil } +// Hash hashes plaintext using a random salt. +func (BcryptHash) Hash(plaintext, _ []byte) ([]byte, error) { + return bcrypt.GenerateFromPassword(plaintext, 14) +} + // ScryptHash implements the scrypt KDF as a hash. type ScryptHash struct { // scrypt's N parameter. If unset or 0, a safe default is used. @@ -113,6 +118,11 @@ func (s ScryptHash) Compare(hashed, plaintext, salt []byte) (bool, error) { return false, nil } +// Hash hashes plaintext using the given salt. +func (s ScryptHash) Hash(plaintext, salt []byte) ([]byte, error) { + return scrypt.Key(plaintext, salt, s.N, s.R, s.P, s.KeyLength) +} + func hashesMatch(pwdHash1, pwdHash2 []byte) bool { return subtle.ConstantTimeCompare(pwdHash1, pwdHash2) == 1 } @@ -121,5 +131,7 @@ func hashesMatch(pwdHash1, pwdHash2 []byte) bool { var ( _ Comparer = (*BcryptHash)(nil) _ Comparer = (*ScryptHash)(nil) + _ Hasher = (*BcryptHash)(nil) + _ Hasher = (*ScryptHash)(nil) _ caddy.Provisioner = (*ScryptHash)(nil) ) -- cgit v1.2.3