diff --git a/cmd/defaults.yaml b/cmd/defaults.yaml index 2eadfa77fd..b95cb6d74b 100644 --- a/cmd/defaults.yaml +++ b/cmd/defaults.yaml @@ -697,6 +697,7 @@ SystemDefaults: # - "bcrypt" # - "md5" # md5Crypt with salt and password shuffling. # - "md5plain" # md5 digest of a password without salt + # - "md5salted" # md5 digest of a salted password # - "scrypt" # - "pbkdf2" # verifier for all pbkdf2 hash modes. SecretHasher: diff --git a/docs/docs/concepts/architecture/secrets.md b/docs/docs/concepts/architecture/secrets.md index 4156f77a26..f8f195114b 100644 --- a/docs/docs/concepts/architecture/secrets.md +++ b/docs/docs/concepts/architecture/secrets.md @@ -70,6 +70,7 @@ The following hash algorithms are supported: - bcrypt (Default) - md5: implementation of md5Crypt with salt and password shuffling [^2] - md5plain: md5 digest of a password without salt [^2] +- md5salted: md5 digest of a salted password [^2] - scrypt - pbkdf2 diff --git a/go.mod b/go.mod index 3f81b16ac5..120450b1ab 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/zitadel/zitadel -go 1.23.4 +go 1.23.7 require ( cloud.google.com/go/profiler v0.4.1 @@ -72,7 +72,7 @@ require ( github.com/twilio/twilio-go v1.22.2 github.com/zitadel/logging v0.6.1 github.com/zitadel/oidc/v3 v3.32.0 - github.com/zitadel/passwap v0.6.0 + github.com/zitadel/passwap v0.7.0 github.com/zitadel/saml v0.3.4 github.com/zitadel/schema v1.3.0 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 @@ -86,12 +86,12 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.29.0 go.opentelemetry.io/otel/trace v1.29.0 go.uber.org/mock v0.5.0 - golang.org/x/crypto v0.31.0 + golang.org/x/crypto v0.36.0 golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 golang.org/x/net v0.33.0 golang.org/x/oauth2 v0.23.0 - golang.org/x/sync v0.11.0 - golang.org/x/text v0.21.0 + golang.org/x/sync v0.12.0 + golang.org/x/text v0.23.0 google.golang.org/api v0.187.0 google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd google.golang.org/grpc v1.65.0 @@ -216,7 +216,7 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect - golang.org/x/sys v0.28.0 + golang.org/x/sys v0.31.0 gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 9c992f3662..8551a6d04d 100644 --- a/go.sum +++ b/go.sum @@ -778,6 +778,8 @@ github.com/zitadel/oidc/v3 v3.32.0 h1:Mw0EPZRC6h+OXAuT0Uk2BZIjJQNHLqUpaJCm6c3IBy github.com/zitadel/oidc/v3 v3.32.0/go.mod h1:DyE/XClysRK/ozFaZSqlYamKVnTh4l6Ln25ihSNI03w= github.com/zitadel/passwap v0.6.0 h1:m9F3epFC0VkBXu25rihSLGyHvWiNlCzU5kk8RoI+SXQ= github.com/zitadel/passwap v0.6.0/go.mod h1:kqAiJ4I4eZvm3Y6oAk6hlEqlZZOkjMHraGXF90GG7LI= +github.com/zitadel/passwap v0.7.0 h1:TQTr9TV75PLATGICor1g5hZDRNHRvB9t0Hn4XkiR7xQ= +github.com/zitadel/passwap v0.7.0/go.mod h1:/NakQNYahdU+YFEitVD6mlm8BLfkiIT+IM5wgClRoAY= github.com/zitadel/saml v0.3.4 h1:L2pybnx2Hs+kqebZmUbnZUd9L/CY2sNw5psMWw2D/6Q= github.com/zitadel/saml v0.3.4/go.mod h1:M0losAULJpLtAmXrYqBnf375ia2rMgJ75b1mpaU/GlA= github.com/zitadel/schema v1.3.0 h1:kQ9W9tvIwZICCKWcMvCEweXET1OcOyGEuFbHs4o5kg0= @@ -846,6 +848,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= @@ -927,6 +931,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -971,6 +977,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -987,6 +995,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= diff --git a/internal/crypto/passwap.go b/internal/crypto/passwap.go index 6ff0f4ea10..e14c2dfaaf 100644 --- a/internal/crypto/passwap.go +++ b/internal/crypto/passwap.go @@ -12,6 +12,7 @@ import ( "github.com/zitadel/passwap/bcrypt" "github.com/zitadel/passwap/md5" "github.com/zitadel/passwap/md5plain" + "github.com/zitadel/passwap/md5salted" "github.com/zitadel/passwap/pbkdf2" "github.com/zitadel/passwap/scrypt" "github.com/zitadel/passwap/verifier" @@ -43,14 +44,15 @@ func (h *Hasher) EncodingSupported(encodedHash string) bool { type HashName string const ( - HashNameArgon2 HashName = "argon2" // used for the common argon2 verifier - HashNameArgon2i HashName = "argon2i" // hash only - HashNameArgon2id HashName = "argon2id" // hash only - HashNameBcrypt HashName = "bcrypt" // hash and verify - HashNameMd5 HashName = "md5" // verify only, as hashing with md5 is insecure and deprecated - HashNameMd5Plain HashName = "md5plain" // verify only, as hashing with md5 is insecure and deprecated - HashNameScrypt HashName = "scrypt" // hash and verify - HashNamePBKDF2 HashName = "pbkdf2" // hash and verify + HashNameArgon2 HashName = "argon2" // used for the common argon2 verifier + HashNameArgon2i HashName = "argon2i" // hash only + HashNameArgon2id HashName = "argon2id" // hash only + HashNameBcrypt HashName = "bcrypt" // hash and verify + HashNameMd5 HashName = "md5" // verify only, as hashing with md5 is insecure and deprecated + HashNameMd5Plain HashName = "md5plain" // verify only, as hashing with md5 is insecure and deprecated + HashNameMd5Salted HashName = "md5salted" // verify only, as hashing with md5 is insecure and deprecated + HashNameScrypt HashName = "scrypt" // hash and verify + HashNamePBKDF2 HashName = "pbkdf2" // hash and verify ) type HashMode string @@ -119,6 +121,10 @@ var knowVerifiers = map[HashName]prefixVerifier{ prefixes: []string{pbkdf2.Prefix}, verifier: pbkdf2.Verifier, }, + HashNameMd5Salted: { + prefixes: []string{md5salted.Prefix}, + verifier: md5salted.Verifier, + }, } func (c *HashConfig) buildVerifiers() (verifiers []verifier.Verifier, prefixes []string, err error) { diff --git a/internal/crypto/passwap_test.go b/internal/crypto/passwap_test.go index cbc7202501..b872b0e298 100644 --- a/internal/crypto/passwap_test.go +++ b/internal/crypto/passwap_test.go @@ -11,6 +11,7 @@ import ( "github.com/zitadel/passwap/argon2" "github.com/zitadel/passwap/bcrypt" "github.com/zitadel/passwap/md5" + "github.com/zitadel/passwap/md5salted" "github.com/zitadel/passwap/pbkdf2" "github.com/zitadel/passwap/scrypt" ) @@ -76,6 +77,7 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { HashNameArgon2, HashNameBcrypt, HashNameMd5, + HashNameMd5Salted, HashNameScrypt, "foobar", }, @@ -122,6 +124,24 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { }, wantErr: true, }, + { + name: "invalid md5plain", + fields: fields{ + Hasher: HasherConfig{ + Algorithm: HashNameMd5Plain, + }, + }, + wantErr: true, + }, + { + name: "invalid md5salted", + fields: fields{ + Hasher: HasherConfig{ + Algorithm: HashNameMd5Salted, + }, + }, + wantErr: true, + }, { name: "invalid argon2", fields: fields{ @@ -160,9 +180,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { "threads": 4, }, }, - Verifiers: []HashName{HashNameBcrypt, HashNameMd5, HashNameScrypt}, + Verifiers: []HashName{HashNameBcrypt, HashNameMd5, HashNameScrypt, HashNameMd5Plain, HashNameMd5Salted}, }, - wantPrefixes: []string{argon2.Prefix, bcrypt.Prefix, md5.Prefix, scrypt.Prefix, scrypt.Prefix_Linux}, + wantPrefixes: []string{argon2.Prefix, bcrypt.Prefix, md5.Prefix, scrypt.Prefix, scrypt.Prefix_Linux, md5salted.Prefix}, }, { name: "argon2id, error", @@ -188,9 +208,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { "threads": 4, }, }, - Verifiers: []HashName{HashNameBcrypt, HashNameMd5, HashNameScrypt}, + Verifiers: []HashName{HashNameBcrypt, HashNameMd5, HashNameScrypt, HashNameMd5Plain, HashNameMd5Salted}, }, - wantPrefixes: []string{argon2.Prefix, bcrypt.Prefix, md5.Prefix, scrypt.Prefix, scrypt.Prefix_Linux}, + wantPrefixes: []string{argon2.Prefix, bcrypt.Prefix, md5.Prefix, scrypt.Prefix, scrypt.Prefix_Linux, md5salted.Prefix}, }, { name: "bcrypt, error", @@ -213,9 +233,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { "cost": 3, }, }, - Verifiers: []HashName{HashNameArgon2, HashNameMd5, HashNameScrypt}, + Verifiers: []HashName{HashNameArgon2, HashNameMd5, HashNameScrypt, HashNameMd5Plain, HashNameMd5Salted}, }, - wantPrefixes: []string{bcrypt.Prefix, argon2.Prefix, md5.Prefix, scrypt.Prefix, scrypt.Prefix_Linux}, + wantPrefixes: []string{bcrypt.Prefix, argon2.Prefix, md5.Prefix, scrypt.Prefix, scrypt.Prefix_Linux, md5salted.Prefix}, }, { name: "scrypt, error", @@ -238,9 +258,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { "cost": 3, }, }, - Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5}, + Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5, HashNameMd5Plain, HashNameMd5Salted}, }, - wantPrefixes: []string{scrypt.Prefix, scrypt.Prefix_Linux, argon2.Prefix, bcrypt.Prefix, md5.Prefix}, + wantPrefixes: []string{scrypt.Prefix, scrypt.Prefix_Linux, argon2.Prefix, bcrypt.Prefix, md5.Prefix, md5salted.Prefix}, }, { name: "pbkdf2, parse error", @@ -277,9 +297,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { "Hash": HashModeSHA1, }, }, - Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5}, + Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5, HashNameMd5Plain, HashNameMd5Salted}, }, - wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix}, + wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix, md5salted.Prefix}, }, { name: "pbkdf2, sha224", @@ -291,9 +311,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { "Hash": HashModeSHA224, }, }, - Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5}, + Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5, HashNameMd5Plain, HashNameMd5Salted}, }, - wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix}, + wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix, md5salted.Prefix}, }, { name: "pbkdf2, sha256", @@ -305,9 +325,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { "Hash": HashModeSHA256, }, }, - Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5}, + Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5, HashNameMd5Plain, HashNameMd5Salted}, }, - wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix}, + wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix, md5salted.Prefix}, }, { name: "pbkdf2, sha384", @@ -319,9 +339,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { "Hash": HashModeSHA384, }, }, - Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5}, + Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5, HashNameMd5Plain, HashNameMd5Salted}, }, - wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix}, + wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix, md5salted.Prefix}, }, { name: "pbkdf2, sha512", @@ -333,9 +353,9 @@ func TestPasswordHashConfig_PasswordHasher(t *testing.T) { "Hash": HashModeSHA512, }, }, - Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5}, + Verifiers: []HashName{HashNameArgon2, HashNameBcrypt, HashNameMd5, HashNameMd5Plain, HashNameMd5Salted}, }, - wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix}, + wantPrefixes: []string{pbkdf2.Prefix, argon2.Prefix, bcrypt.Prefix, md5.Prefix, md5salted.Prefix}, }, } for _, tt := range tests {