From ac87035742cb428dbd5dc152ecc2d03a388d042e Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 10 Sep 2024 18:23:06 +0200 Subject: [PATCH] fix(auth): restrict max password length to 72 bytes Bcrypt allows a maximum of 72 bytes. This is part of the algorithm and not something we could change in Vikunja. The solution here was to restrict the password during registration to a max length of 72 bytes. In the future, this should be changed to hash passwords with sha512 or similar before hashing them with bcrypt. Because they should also be salted in that case and the added complexity during the migration phase, this was not implemented yet. The change in this commit only improves the error handling to return an input error instead of a server error when the user enters a password > 72 bytes. Resolves https://vikunja.sentry.io/share/issue/e8e0b64612d84504942feee002ac498a/ (cherry picked from commit 44a43b9f8616f11560c9e04f88f3000a6df5338d) --- frontend/src/helpers/validatePasswort.ts | 2 +- frontend/src/i18n/lang/en.json | 2 +- pkg/user/user.go | 8 ++++---- pkg/user/{username_valid.go => validator.go} | 12 +++++++++++- 4 files changed, 17 insertions(+), 7 deletions(-) rename pkg/user/{username_valid.go => validator.go} (85%) diff --git a/frontend/src/helpers/validatePasswort.ts b/frontend/src/helpers/validatePasswort.ts index 0414d5ee3..c522d07a7 100644 --- a/frontend/src/helpers/validatePasswort.ts +++ b/frontend/src/helpers/validatePasswort.ts @@ -7,7 +7,7 @@ export function validatePassword(password: string, validateMinLength: boolean = return 'user.auth.passwordNotMin' } - if (validateMinLength && password.length > 250) { + if (validateMinLength && password.length > 72) { return 'user.auth.passwordNotMax' } diff --git a/frontend/src/i18n/lang/en.json b/frontend/src/i18n/lang/en.json index 6499f708a..0cc192b8c 100644 --- a/frontend/src/i18n/lang/en.json +++ b/frontend/src/i18n/lang/en.json @@ -61,7 +61,7 @@ "usernameMustNotLookLikeUrl": "The username must not look like a URL.", "passwordRequired": "Please provide a password.", "passwordNotMin": "Password must have at least 8 characters.", - "passwordNotMax": "Password must have at most 250 characters.", + "passwordNotMax": "Password must have at most 72 characters.", "showPassword": "Show the password", "hidePassword": "Hide the password", "noAccountYet": "Don't have an account yet?", diff --git a/pkg/user/user.go b/pkg/user/user.go index 6f9cfe4d7..3240a485a 100644 --- a/pkg/user/user.go +++ b/pkg/user/user.go @@ -197,8 +197,8 @@ type APIUserPassword struct { ID int64 `json:"id"` // The user's username. Cannot contain anything that looks like an url or whitespaces. Username string `json:"username" valid:"length(3|250),username" minLength:"3" maxLength:"250"` - // The user's password in clear text. Only used when registering the user. - Password string `json:"password" valid:"length(8|250)" minLength:"8" maxLength:"250"` + // The user's password in clear text. Only used when registering the user. The maximum limi is 72 bytes, which may be less than 72 characters. This is due to the limit in the bcrypt hashing algorithm used to store passwords in Vikunja. + Password string `json:"password" valid:"bcrypt_password" minLength:"8" maxLength:"72"` // The user's email address Email string `json:"email" valid:"email,length(0|250)" maxLength:"250"` } @@ -213,7 +213,7 @@ func (apiUser *APIUserPassword) APIFormat() *User { } } -// GetUserByID gets informations about a user by its ID +// GetUserByID returns user by its ID func GetUserByID(s *xorm.Session, id int64) (user *User, err error) { // Apparently xorm does otherwise look for all users but return only one, which leads to returning one even if the ID is 0 if id < 1 { @@ -223,7 +223,7 @@ func GetUserByID(s *xorm.Session, id int64) (user *User, err error) { return getUser(s, &User{ID: id}, false) } -// GetUserByUsername gets a user from its user name. This is an extra function to be able to add an extra error check. +// GetUserByUsername gets a user from its username. This is an extra function to be able to add an extra error check. func GetUserByUsername(s *xorm.Session, username string) (user *User, err error) { if username == "" { return &User{}, ErrUserDoesNotExist{} diff --git a/pkg/user/username_valid.go b/pkg/user/validator.go similarity index 85% rename from pkg/user/username_valid.go rename to pkg/user/validator.go index 0ac77ea19..160435fd4 100644 --- a/pkg/user/username_valid.go +++ b/pkg/user/validator.go @@ -16,7 +16,9 @@ package user -import "github.com/asaskevich/govalidator" +import ( + "github.com/asaskevich/govalidator" +) func init() { govalidator.TagMap["username"] = func(i string) bool { @@ -33,4 +35,12 @@ func init() { return true } + + govalidator.TagMap["bcrypt_password"] = func(str string) bool { + if len(str) < 8 { + return false + } + + return len([]byte(str)) < 72 + } }