mirror of
https://github.com/caddyserver/caddy.git
synced 2025-08-11 09:16:26 -04:00
bcrypt: add cost parameter to hash-password (#7149)
* feat: add bcrypt cost parameter to hash-password * revert: typos * refactor: take the cost out of interface * fix: default bcrypt cost to 14 * fix: follow bcrypt library for min and max cost * doc: mention defaultBcryptCost in cost parameter description * chore: gci format * fix: more specific bcrypt cost algorithm flag * feat: bcrypt cost provisioning * Revert "feat: bcrypt cost provisioning" This reverts commit e09d4bd036e7657588ed7785afd2c5388b29fb2a. * chore: gci format * chore: gci format * chore: gci format * chore: golangcilint fmted --------- Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>
This commit is contained in:
parent
19ff47a63b
commit
49dac61b07
@ -60,7 +60,8 @@ func (Authentication) CaddyModule() caddy.ModuleInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provision sets up a.
|
// Provision sets up an Authentication module by initializing its logger,
|
||||||
|
// loading and registering all configured authentication providers.
|
||||||
func (a *Authentication) Provision(ctx caddy.Context) error {
|
func (a *Authentication) Provision(ctx caddy.Context) error {
|
||||||
a.logger = ctx.Logger()
|
a.logger = ctx.Logger()
|
||||||
a.Providers = make(map[string]Authenticator)
|
a.Providers = make(map[string]Authenticator)
|
||||||
|
@ -32,7 +32,7 @@ import (
|
|||||||
func init() {
|
func init() {
|
||||||
caddycmd.RegisterCommand(caddycmd.Command{
|
caddycmd.RegisterCommand(caddycmd.Command{
|
||||||
Name: "hash-password",
|
Name: "hash-password",
|
||||||
Usage: "[--plaintext <password>] [--algorithm <name>]",
|
Usage: "[--plaintext <password>] [--algorithm <name>] [--cost <difficulty>]",
|
||||||
Short: "Hashes a password and writes base64",
|
Short: "Hashes a password and writes base64",
|
||||||
Long: `
|
Long: `
|
||||||
Convenient way to hash a plaintext password. The resulting
|
Convenient way to hash a plaintext password. The resulting
|
||||||
@ -43,10 +43,17 @@ Caddy is attached to a controlling tty, the plaintext will
|
|||||||
not be echoed.
|
not be echoed.
|
||||||
|
|
||||||
--algorithm currently only supports 'bcrypt', and is the default.
|
--algorithm currently only supports 'bcrypt', and is the default.
|
||||||
|
|
||||||
|
--cost sets the bcrypt hashing difficulty.
|
||||||
|
Higher values increase security by making the hash computation slower and more CPU-intensive.
|
||||||
|
If the provided cost is not within the valid range [bcrypt.MinCost, bcrypt.MaxCost],
|
||||||
|
the default value (defaultBcryptCost) will be used instead.
|
||||||
|
Note: Higher cost values can significantly degrade performance on slower systems.
|
||||||
`,
|
`,
|
||||||
CobraFunc: func(cmd *cobra.Command) {
|
CobraFunc: func(cmd *cobra.Command) {
|
||||||
cmd.Flags().StringP("plaintext", "p", "", "The plaintext password")
|
cmd.Flags().StringP("plaintext", "p", "", "The plaintext password")
|
||||||
cmd.Flags().StringP("algorithm", "a", "bcrypt", "Name of the hash algorithm")
|
cmd.Flags().StringP("algorithm", "a", "bcrypt", "Name of the hash algorithm")
|
||||||
|
cmd.Flags().Int("bcrypt-cost", defaultBcryptCost, "Bcrypt hashing cost (only used with 'bcrypt' algorithm)")
|
||||||
cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdHashPassword)
|
cmd.RunE = caddycmd.WrapCommandFuncForCobra(cmdHashPassword)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -57,6 +64,7 @@ func cmdHashPassword(fs caddycmd.Flags) (int, error) {
|
|||||||
|
|
||||||
algorithm := fs.String("algorithm")
|
algorithm := fs.String("algorithm")
|
||||||
plaintext := []byte(fs.String("plaintext"))
|
plaintext := []byte(fs.String("plaintext"))
|
||||||
|
bcryptCost := fs.Int("bcrypt-cost")
|
||||||
|
|
||||||
if len(plaintext) == 0 {
|
if len(plaintext) == 0 {
|
||||||
fd := int(os.Stdin.Fd())
|
fd := int(os.Stdin.Fd())
|
||||||
@ -108,7 +116,7 @@ func cmdHashPassword(fs caddycmd.Flags) (int, error) {
|
|||||||
var hashString string
|
var hashString string
|
||||||
switch algorithm {
|
switch algorithm {
|
||||||
case "bcrypt":
|
case "bcrypt":
|
||||||
hash, err = BcryptHash{}.Hash(plaintext)
|
hash, err = BcryptHash{cost: bcryptCost}.Hash(plaintext)
|
||||||
hashString = string(hash)
|
hashString = string(hash)
|
||||||
default:
|
default:
|
||||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("unrecognized hash algorithm: %s", algorithm)
|
return caddy.ExitCodeFailedStartup, fmt.Errorf("unrecognized hash algorithm: %s", algorithm)
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
package caddyauth
|
package caddyauth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
"github.com/caddyserver/caddy/v2"
|
||||||
@ -24,8 +26,15 @@ func init() {
|
|||||||
caddy.RegisterModule(BcryptHash{})
|
caddy.RegisterModule(BcryptHash{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// defaultBcryptCost cost 14 strikes a solid balance between security, usability, and hardware performance
|
||||||
|
const defaultBcryptCost = 14
|
||||||
|
|
||||||
// BcryptHash implements the bcrypt hash.
|
// BcryptHash implements the bcrypt hash.
|
||||||
type BcryptHash struct{}
|
type BcryptHash struct {
|
||||||
|
// cost is the bcrypt hashing difficulty factor (work factor).
|
||||||
|
// Higher values increase computation time and security.
|
||||||
|
cost int
|
||||||
|
}
|
||||||
|
|
||||||
// CaddyModule returns the Caddy module information.
|
// CaddyModule returns the Caddy module information.
|
||||||
func (BcryptHash) CaddyModule() caddy.ModuleInfo {
|
func (BcryptHash) CaddyModule() caddy.ModuleInfo {
|
||||||
@ -38,7 +47,7 @@ func (BcryptHash) CaddyModule() caddy.ModuleInfo {
|
|||||||
// Compare compares passwords.
|
// Compare compares passwords.
|
||||||
func (BcryptHash) Compare(hashed, plaintext []byte) (bool, error) {
|
func (BcryptHash) Compare(hashed, plaintext []byte) (bool, error) {
|
||||||
err := bcrypt.CompareHashAndPassword(hashed, plaintext)
|
err := bcrypt.CompareHashAndPassword(hashed, plaintext)
|
||||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
if errors.Is(err, bcrypt.ErrMismatchedHashAndPassword) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -48,8 +57,13 @@ func (BcryptHash) Compare(hashed, plaintext []byte) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hash hashes plaintext using a random salt.
|
// Hash hashes plaintext using a random salt.
|
||||||
func (BcryptHash) Hash(plaintext []byte) ([]byte, error) {
|
func (b BcryptHash) Hash(plaintext []byte) ([]byte, error) {
|
||||||
return bcrypt.GenerateFromPassword(plaintext, 14)
|
cost := b.cost
|
||||||
|
if cost < bcrypt.MinCost || cost > bcrypt.MaxCost {
|
||||||
|
cost = defaultBcryptCost
|
||||||
|
}
|
||||||
|
|
||||||
|
return bcrypt.GenerateFromPassword(plaintext, cost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FakeHash returns a fake hash.
|
// FakeHash returns a fake hash.
|
||||||
|
@ -122,7 +122,6 @@ func TestPreferOrder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func TestValidate(t *testing.T) {
|
func TestValidate(t *testing.T) {
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
name string
|
name string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user