From 6bc09d799c8468a3d0f675a8f2459226b0afcfaf Mon Sep 17 00:00:00 2001 From: WaterLemons2k <62788816+WaterLemons2k@users.noreply.github.com> Date: Fri, 21 Jul 2023 14:43:58 +0800 Subject: [PATCH] feat(web): validate password (#779) * feat(web): validate password https://github.com/wagslane/go-password-validator * fix: validate password only if password is non-empty --- go.mod | 5 ++- go.sum | 2 ++ web/password.go | 91 +++++++++++++++++++++++++++++++++++++++++++++++++ web/save.go | 8 +++++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 web/password.go diff --git a/go.mod b/go.mod index fe09ad8d3..d3b2554c9 100644 --- a/go.mod +++ b/go.mod @@ -7,4 +7,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) -require golang.org/x/sys v0.6.0 // indirect +require ( + github.com/wagslane/go-password-validator v0.3.0 // indirect + golang.org/x/sys v0.6.0 // indirect +) diff --git a/go.sum b/go.sum index b6feeb5a2..1762919f0 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= +github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSVP061Ry2PX0/ON6I= +github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/web/password.go b/web/password.go new file mode 100644 index 000000000..80fd860c9 --- /dev/null +++ b/web/password.go @@ -0,0 +1,91 @@ +package web + +import ( + "errors" + "fmt" + "strings" + + passwordvalidator "github.com/wagslane/go-password-validator" +) + +const ( + replaceChars = `!@$&*` + sepChars = `_-., ` + otherSpecialChars = `"#%'()+/:;<=>?[\]^{|}~` + lowerChars = `abcdefghijklmnopqrstuvwxyz` + upperChars = `ABCDEFGHIJKLMNOPQRSTUVWXYZ` + digitsChars = `0123456789` +) + +// validate 检查密码强度是否大于最低要求(50)。如果不是则返回错误并说明如何加强密码。向客户端显示此错误是安全的。 +func validate(password string) error { + return validatePassword(password, 50) +} + +// validatePassword 在密码大于或等于 minEntropy 时返回 nil。如果不是则返回错误。 +// 这解释了如何加强密码。向客户端显示此错误是安全的。 +// +// https://github.com/wagslane/go-password-validator/blob/v0.3.0/validate.go#L13 +func validatePassword(password string, minEntropy float64) error { + entropy := passwordvalidator.GetEntropy(password) + if entropy >= minEntropy { + return nil + } + + hasReplace := false + hasSep := false + hasOtherSpecial := false + hasLower := false + hasUpper := false + hasDigits := false + for _, c := range password { + if strings.ContainsRune(replaceChars, c) { + hasReplace = true + continue + } + if strings.ContainsRune(sepChars, c) { + hasSep = true + continue + } + if strings.ContainsRune(otherSpecialChars, c) { + hasOtherSpecial = true + continue + } + if strings.ContainsRune(lowerChars, c) { + hasLower = true + continue + } + if strings.ContainsRune(upperChars, c) { + hasUpper = true + continue + } + if strings.ContainsRune(digitsChars, c) { + hasDigits = true + continue + } + } + + allMessages := []string{} + + if !hasOtherSpecial || !hasSep || !hasReplace { + allMessages = append(allMessages, "包含更多特殊字符") + } + if !hasLower { + allMessages = append(allMessages, "使用小写字母") + } + if !hasUpper { + allMessages = append(allMessages, "使用大写字母") + } + if !hasDigits { + allMessages = append(allMessages, "使用数字") + } + + if len(allMessages) > 0 { + return fmt.Errorf( + "密码不安全!尝试%v或使用更长的密码", + strings.Join(allMessages, ","), + ) + } + + return errors.New("密码不安全!尝试使用更长的密码") +} diff --git a/web/save.go b/web/save.go index cbf0f1473..d70571914 100755 --- a/web/save.go +++ b/web/save.go @@ -50,6 +50,14 @@ func checkAndSave(request *http.Request) string { } + // 如果密码不为空则检查是否够强 + if passwordNew != "" { + err = validate(passwordNew) + if err != nil { + return err.Error() + } + } + conf.NotAllowWanAccess = request.FormValue("NotAllowWanAccess") == "on" conf.Username = usernameNew conf.Password = passwordNew