From 8ae885874b5702618a3e5155df8335a0988feb6c Mon Sep 17 00:00:00 2001 From: naiba Date: Fri, 16 Dec 2022 23:34:14 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=EF=B8=8F=20more=20secure=20token?= =?UTF-8?q?=20generation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 2 +- README.md | 2 +- cmd/dashboard/controller/member_api.go | 18 ++++++--- cmd/dashboard/controller/oauth2.go | 22 ++++++++++- model/user.go | 8 ---- pkg/utils/utils.go | 54 ++++++++------------------ pkg/utils/utils_test.go | 11 ++++++ script/install.sh | 2 +- script/install_en.sh | 2 +- service/singleton/singleton.go | 2 +- 10 files changed, 65 insertions(+), 58 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e31e49b..3fd6adf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,4 +31,4 @@ jobs: - name: Run Gosec Security Scanner run: | go install github.com/securego/gosec/v2/cmd/gosec@latest - gosec -exclude=G104,G404 ./... + gosec -exclude=G104 ./... diff --git a/README.md b/README.md index 1353616..b01ae6c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@
LOGO designed by 熊大 .

-    +   

:trollface: Nezha Monitoring: Self-hosted, lightweight server and website monitoring and O&M tool.

diff --git a/cmd/dashboard/controller/member_api.go b/cmd/dashboard/controller/member_api.go index 01144e6..fb4d5ba 100644 --- a/cmd/dashboard/controller/member_api.go +++ b/cmd/dashboard/controller/member_api.go @@ -100,9 +100,17 @@ func (ma *memberAPI) issueNewToken(c *gin.Context) { }) return } + secureToken, err := utils.GenerateRandomString(32) + if err != nil { + c.JSON(http.StatusOK, model.Response{ + Code: http.StatusBadRequest, + Message: fmt.Sprintf("请求错误:%s", err), + }) + return + } token := &model.ApiToken{ UserID: u.ID, - Token: utils.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login)), + Token: secureToken, Note: tf.Note, } singleton.DB.Create(token) @@ -310,7 +318,6 @@ type serverForm struct { } func (ma *memberAPI) addOrEditServer(c *gin.Context) { - admin := c.MustGet(model.CtxKeyAuthorizedUser).(*model.User) var sf serverForm var s model.Server var isEdit bool @@ -324,9 +331,10 @@ func (ma *memberAPI) addOrEditServer(c *gin.Context) { s.Note = sf.Note s.HideForGuest = sf.HideForGuest == "on" if s.ID == 0 { - s.Secret = utils.MD5(fmt.Sprintf("%s%s%d", time.Now(), sf.Name, admin.ID)) - s.Secret = s.Secret[:18] - err = singleton.DB.Create(&s).Error + s.Secret, err = utils.GenerateRandomString(18) + if err == nil { + err = singleton.DB.Create(&s).Error + } } else { isEdit = true err = singleton.DB.Save(&s).Error diff --git a/cmd/dashboard/controller/oauth2.go b/cmd/dashboard/controller/oauth2.go index 4b11815..99e064f 100644 --- a/cmd/dashboard/controller/oauth2.go +++ b/cmd/dashboard/controller/oauth2.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" "strings" + "time" "code.gitea.io/sdk/gitea" "github.com/gin-gonic/gin" @@ -92,7 +93,15 @@ func (oa *oauth2controller) getRedirectURL(c *gin.Context) string { } func (oa *oauth2controller) login(c *gin.Context) { - randomString := utils.RandStringBytesMaskImprSrcUnsafe(32) + randomString, err := utils.GenerateRandomString(32) + if err != nil { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusBadRequest, + Title: "Something Wrong", + Msg: err.Error(), + }, true) + return + } state, stateKey := randomString[:16], randomString[16:] singleton.Cache.Set(fmt.Sprintf("%s%s", model.CacheKeyOauth2State, stateKey), state, cache.DefaultExpiration) url := oa.getCommonOauth2Config(c).AuthCodeURL(state, oauth2.AccessTypeOnline) @@ -195,7 +204,16 @@ func (oa *oauth2controller) callback(c *gin.Context) { }, true) return } - user.IssueNewToken() + user.Token, err = utils.GenerateRandomString(32) + if err != nil { + mygin.ShowErrorPage(c, mygin.ErrInfo{ + Code: http.StatusBadRequest, + Title: "Something wrong", + Msg: err.Error(), + }, true) + return + } + user.TokenExpired = time.Now().AddDate(0, 2, 0) singleton.DB.Save(&user) c.SetCookie(singleton.Conf.Site.CookieName, user.Token, 60*60*24, "", "", false, false) c.HTML(http.StatusOK, "dashboard-"+singleton.Conf.Site.DashboardTheme+"/redirect", mygin.CommonEnvironment(c, gin.H{ diff --git a/model/user.go b/model/user.go index 935f13f..11579eb 100644 --- a/model/user.go +++ b/model/user.go @@ -1,14 +1,11 @@ package model import ( - "fmt" "time" "code.gitea.io/sdk/gitea" "github.com/google/go-github/v47/github" "github.com/xanzy/go-gitlab" - - "github.com/naiba/nezha/pkg/utils" ) type User struct { @@ -72,8 +69,3 @@ func NewUserFromGitHub(gu *github.User) User { u.Bio = gu.GetBio() return u } - -func (u *User) IssueNewToken() { - u.Token = utils.MD5(fmt.Sprintf("%d%d%s", time.Now().UnixNano(), u.ID, u.Login)) - u.TokenExpired = time.Now().AddDate(0, 2, 0) -} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 5f97b57..80b1436 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -1,53 +1,17 @@ package utils import ( - "crypto/md5" // #nosec - "encoding/hex" - "math/rand" + "crypto/rand" + "math/big" "os" "regexp" "strings" - "time" - "unsafe" jsoniter "github.com/json-iterator/go" ) var Json = jsoniter.ConfigCompatibleWithStandardLibrary -const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -const ( - letterIdxBits = 6 // 6 bits to represent a letter index - letterIdxMask = 1<= 0; { - if remain == 0 { - cache, remain = src.Int63(), letterIdxMax - } - if idx := int(cache & letterIdxMask); idx < len(letterBytes) { - b[i] = letterBytes[idx] - i-- - } - cache >>= letterIdxBits - remain-- - } - - return *(*string)(unsafe.Pointer(&b)) //#nosec -} - -func MD5(plantext string) string { - hash := md5.New() // #nosec - hash.Write([]byte(plantext)) - return hex.EncodeToString(hash.Sum(nil)) -} - func IsWindows() bool { return os.PathSeparator == '\\' && os.PathListSeparator == ';' } @@ -98,3 +62,17 @@ func IsFileExists(path string) bool { _, err := os.Stat(path) return err == nil } + +func GenerateRandomString(n int) (string, error) { + const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + lettersLength := big.NewInt(int64(len(letters))) + ret := make([]byte, n) + for i := 0; i < n; i++ { + num, err := rand.Int(rand.Reader, lettersLength) + if err != nil { + return "", err + } + ret[i] = letters[num.Int64()] + } + return string(ret), nil +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 688a703..87d3847 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -39,3 +39,14 @@ func TestNotification(t *testing.T) { assert.Equal(t, IPDesensitize(c.input), c.output) } } + +func TestGenerGenerateRandomString(t *testing.T) { + generatedString := make(map[string]bool) + for i := 0; i < 100; i++ { + str, err := GenerateRandomString(32) + assert.Nil(t, err) + assert.Equal(t, len(str), 32) + assert.False(t, generatedString[str]) + generatedString[str] = true + } +} diff --git a/script/install.sh b/script/install.sh index 1c74e6f..d29c778 100755 --- a/script/install.sh +++ b/script/install.sh @@ -11,7 +11,7 @@ NZ_BASE_PATH="/opt/nezha" NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard" NZ_AGENT_PATH="${NZ_BASE_PATH}/agent" NZ_AGENT_SERVICE="/etc/systemd/system/nezha-agent.service" -NZ_VERSION="v0.11.0" +NZ_VERSION="v0.11.1" red='\033[0;31m' green='\033[0;32m' diff --git a/script/install_en.sh b/script/install_en.sh index aee4e3e..c20d37b 100755 --- a/script/install_en.sh +++ b/script/install_en.sh @@ -11,7 +11,7 @@ NZ_BASE_PATH="/opt/nezha" NZ_DASHBOARD_PATH="${NZ_BASE_PATH}/dashboard" NZ_AGENT_PATH="${NZ_BASE_PATH}/agent" NZ_AGENT_SERVICE="/etc/systemd/system/nezha-agent.service" -NZ_VERSION="v0.11.0" +NZ_VERSION="v0.11.1" red='\033[0;31m' green='\033[0;32m' diff --git a/service/singleton/singleton.go b/service/singleton/singleton.go index cd1956a..88c8e30 100644 --- a/service/singleton/singleton.go +++ b/service/singleton/singleton.go @@ -12,7 +12,7 @@ import ( "github.com/naiba/nezha/pkg/utils" ) -var Version = "v0.14.7" // !!记得修改 README 中的 badge 版本!! +var Version = "v0.14.8" // !!记得修改 README 中的 badge 版本!! var ( Conf *model.Config