CVE-2023-4125 (published 2023-08-03T04:15Z)

Weak Password Requirements in GitHub repository answerdev/answer prior to v1.1.0.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-521
cwe_name: Weak Password Requirements
description: The product does not require that users should have strong passwords, which makes it easier for attackers to compromise user accounts.
extended_description: Authentication mechanisms often rely on a memorized secret (also known as a password) to provide an assertion of identity for a user of a system. It is therefore important that this password be of sufficient complexity and impractical for an adversary to guess. The specific requirements around how complex a password needs to be depends on the type of system being protected. Selecting the correct password requirements and enforcing them through implementation are critical to the overall success of the authentication mechanism.
         

Fix information:
repo_url: https://github.com/answerdev/answer
commit_details: repo_url: https://github.com/answerdev/answer
author: LinkinStars
author_date: 2023-05-22 15:09:22+08:00
msg: fix(password): password can't contains space.
repository: repo_url: https://github.com/answerdev/answer
repo_name: apache/incubator-answer
description: A Q&A platform software for teams at any scales. Whether it's a community forum, help center, or knowledge management platform, you can always count on Apache Answer.
repo_language: Go
owner: answerdev
file_changes: filename: en_US.yaml
change_type: ModificationType.MODIFY
diff: @@ -1,19 +1,4 @@
 # The following fields are used for back-end
-#  backend.email.tpl.change_email.title.other = Confirm your new email address for {{.SiteName}}  by clicking on the following link:\u003cbr\u003e\u003cbr\u003e\n\n\u003ca href='{{.ChangeEmailUrl}}' target='_blank'\u003e{{.ChangeEmailUrl}}\u003c/a\u003e\u003cbr\u003e\u003cbr\u003e\n\nIf you did not request this change, please ignore this email.\n
-#  backend.email.tpl.change_email.body.other = [{{.SiteName}}] Confirm your new email address
-#  backend.email.tpl.new_answer.title.other = \u003cstrong\u003e\u003ca href='{{.AnswerUrl}}'\u003e{{.QuestionTitle}}\u003c/a\u003e\u003c/strong\u003e\u003cbr\u003e\u003cbr\u003e\n\n\u003csmall\u003e{{.DisplayName}}:\u003c/small\u003e\u003cbr\u003e\n\u003cblockquote\u003e{{.AnswerSummary}}\u003c/blockquote\u003e\u003cbr\u003e\n\u003ca href='{{.AnswerUrl}}'\u003eView it on {{.SiteName}}\u003c/a\u003e\u003cbr\u003e\u003cbr\u003e\n\n\u003csmall\u003eYou are receiving this because you authored the thread. \u003ca href='{{.UnsubscribeUrl}}'\u003eUnsubscribe\u003c/a\u003e\u003c/small\u003e
-#  backend.email.tpl.new_answer.body.other = [{{.SiteName}}] {{.DisplayName}} answered your question
-#  backend.email.tpl.new_comment.title.other = \u003cstrong\u003e\u003ca href='{{.CommentUrl}}'\u003e{{.QuestionTitle}}\u003c/a\u003e\u003c/strong\u003e\u003cbr\u003e\u003cbr\u003e\n\n\u003csmall\u003e{{.DisplayName}}:\u003c/small\u003e\u003cbr\u003e\n\u003cblockquote\u003e{{.CommentSummary}}\u003c/blockquote\u003e\u003cbr\u003e\n\u003ca href='{{.CommentUrl}}'\u003eView it on {{.SiteName}}\u003c/a\u003e\u003cbr\u003e\u003cbr\u003e\n\n\u003csmall\u003eYou are receiving this because you authored the thread. \u003ca href='{{.UnsubscribeUrl}}'\u003eUnsubscribe\u003c/a\u003e\u003c/small\u003e
-#  backend.email.tpl.new_comment.body.other = [{{.SiteName}}] {{.DisplayName}} commented on your post
-#  backend.email.tpl.pass_reset.title.other = Somebody asked to reset your password on [{{.SiteName}}].\u003cbr\u003e\u003cbr\u003e\n\nIf it was not you, you can safely ignore this email.\u003cbr\u003e\u003cbr\u003e\n\nClick the following link to choose a new password:\u003cbr\u003e\n\u003ca href='{{.PassResetUrl}}' target='_blank'\u003e{{.PassResetUrl}}\u003c/a\u003e\n
-#  backend.email.tpl.pass_reset.body.other = [{{.SiteName }}] Password reset
-#  backend.email.tpl.register.title.other = Welcome to {{.SiteName}}\u003cbr\u003e\u003cbr\u003e\n\nClick the following link to confirm and activate your new account:\u003cbr\u003e\n\u003ca href='{{.RegisterUrl}}' target='_blank'\u003e{{.RegisterUrl}}\u003c/a\u003e\u003cbr\u003e\u003cbr\u003e\n\nIf the above link is not clickable, try copying and pasting it into the address bar of your web browser.\n
-#  backend.email.tpl.register.body.other = [{{.SiteName}}] Confirm your new account
-#  backend.email.tpl.test.title.other = This is a test email.
-#  backend.email.tpl.test.body.other = [{{.SiteName}}] Test Email
-
-
-
 backend:
   base:
     success:
@@ -123,6 +108,9 @@ backend:
   email_or_password_wrong_error:
     other: Email and password do not match.
   error:
+    password:
+      space_invalid:
+        other: Password cannot contain spaces.
     admin:
       cannot_update_their_password:
         other: You cannot modify your password.

programming_language: Python
filename: zh_CN.yaml
change_type: ModificationType.MODIFY
diff: @@ -97,13 +97,16 @@ backend:
       other: 编辑标签描述（无需审核）
     rank_tag_synonym_label:
       other: 管理标签同义词
-  email:
+  e_mail:
     other: 邮箱
   password:
     other: 密码
   email_or_password_wrong_error:
     other: 邮箱和密码不匹配。
   error:
+    password:
+      space_invalid:
+        other: 密码不能包含空格。
     admin:
       cannot_update_their_password:
         other: 您无法修改自己的密码。

programming_language: Python
filename: validator.go
change_type: ModificationType.MODIFY
diff: @@ -207,10 +207,12 @@ func (m *MyValidator) Check(value interface{}) (errFields []*FormErrorField, err
 		if err == nil {
 			return nil, nil
 		}
+		errMsg := ""
 		for _, errField := range errFields {
 			errField.ErrorMsg = translator.Tr(m.Lang, errField.ErrorMsg)
+			errMsg = errField.ErrorMsg
 		}
-		return errFields, err
+		return errFields, myErrors.BadRequest(reason.RequestFormatError).WithMsg(errMsg)
 	}
 	return nil, nil
 }

programming_language: Go
filename: v10.go
change_type: ModificationType.MODIFY
diff: @@ -24,6 +24,8 @@ func addLoginLimitations(x *xorm.Engine) error {
 		_ = json.Unmarshal([]byte(loginSiteInfo.Content), content)
 		content.AllowEmailRegistrations = true
 		content.AllowEmailDomains = make([]string, 0)
+		data, _ := json.Marshal(content)
+		loginSiteInfo.Content = string(data)
 		_, err = x.ID(loginSiteInfo.ID).Cols("content").Update(loginSiteInfo)
 		if err != nil {
 			return fmt.Errorf("update site info failed: %w", err)

programming_language: Go
filename: user_schema.go
change_type: ModificationType.MODIFY
diff: @@ -262,35 +262,29 @@ type UserRegisterReq struct {
 }
 
 func (u *UserRegisterReq) Check() (errFields []*validator.FormErrorField, err error) {
-	// TODO i18n
-	err = checker.CheckPassword(8, 32, 0, u.Pass)
-	if err != nil {
-		errField := &validator.FormErrorField{
+	if err = checker.CheckPassword(u.Pass); err != nil {
+		errFields = append(errFields, &validator.FormErrorField{
 			ErrorField: "pass",
 			ErrorMsg:   err.Error(),
-		}
-		errFields = append(errFields, errField)
+		})
 		return errFields, err
 	}
 	return nil, nil
 }
 
 type UserModifyPasswordReq struct {
-	OldPass string `validate:"omitempty,gte=8,lte=32" json:"old_pass"`
-	Pass    string `validate:"required,gte=8,lte=32" json:"pass"`
+	OldPass     string `validate:"omitempty,gte=8,lte=32" json:"old_pass"`
+	Pass        string `validate:"required,gte=8,lte=32" json:"pass"`
 	UserID      string `json:"-"`
 	AccessToken string `json:"-"`
 }
 
 func (u *UserModifyPasswordReq) Check() (errFields []*validator.FormErrorField, err error) {
-	// TODO i18n
-	err = checker.CheckPassword(8, 32, 0, u.Pass)
-	if err != nil {
-		errField := &validator.FormErrorField{
+	if err = checker.CheckPassword(u.Pass); err != nil {
+		errFields = append(errFields, &validator.FormErrorField{
 			ErrorField: "pass",
 			ErrorMsg:   err.Error(),
-		}
-		errFields = append(errFields, errField)
+		})
 		return errFields, err
 	}
 	return nil, nil
@@ -352,14 +346,11 @@ type UserRePassWordRequest struct {
 }
 
 func (u *UserRePassWordRequest) Check() (errFields []*validator.FormErrorField, err error) {
-	// TODO i18n
-	err = checker.CheckPassword(8, 32, 0, u.Pass)
-	if err != nil {
-		errField := &validator.FormErrorField{
+	if err = checker.CheckPassword(u.Pass); err != nil {
+		errFields = append(errFields, &validator.FormErrorField{
 			ErrorField: "pass",
 			ErrorMsg:   err.Error(),
-		}
-		errFields = append(errFields, errField)
+		})
 		return errFields, err
 	}
 	return nil, nil

programming_language: Go
filename: password.go
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,7 @@ package checker
 import (
 	"fmt"
 	"regexp"
+	"strings"
 )
 
 const (
@@ -13,27 +14,26 @@ const (
 	LevelS
 )
 
-// CheckPassword
-// minLength: Specifies the minimum length of a password
-// maxLength：Specifies the maximum length of a password
-// minLevel：Specifies the minimum strength level required for passwords
-// pwd：Text passwords
-func CheckPassword(minLength, maxLength, minLevel int, pwd string) error {
-	// First check whether the password length is within the range
-	if len(pwd) < minLength {
-		return fmt.Errorf("BAD PASSWORD: The password is shorter than %d characters", minLength)
-	}
-	if len(pwd) > maxLength {
-		return fmt.Errorf("BAD PASSWORD: The password is logner than %d characters", maxLength)
+const (
+	PasswordCannotContainSpaces = "error.password.space_invalid"
+)
+
+// CheckPassword checks the password strength
+func CheckPassword(password string) error {
+	if strings.Contains(password, " ") {
+		return fmt.Errorf(PasswordCannotContainSpaces)
 	}
 
+	// TODO Currently there is no requirement for password strength
+	minLevel := 0
+
 	// The password strength level is initialized to D.
 	// The regular is used to verify the password strength.
 	// If the matching is successful, the password strength increases by 1
 	level := levelD
 	patternList := []string{`[0-9]+`, `[a-z]+`, `[A-Z]+`, `[~!@#$%^&*?_-]+`}
 	for _, pattern := range patternList {
-		match, _ := regexp.MatchString(pattern, pwd)
+		match, _ := regexp.MatchString(pattern, password)
 		if match {
 			level++
 		}

programming_language: Go