From 4ff8cc4ed275ded22a3337657492b24ffd1cbf6c Mon Sep 17 00:00:00 2001 From: Mehdy javany Date: Thu, 16 Dec 2021 21:10:02 +0330 Subject: [PATCH 01/16] Hash user API Keys --- go.mod | 9 +++++++-- go.sum | 10 +++++++++ storage/database/user.go | 22 +++++++++++++++++--- sys/model/apikey.go | 24 ++++++++++++---------- sys/model/user_test.go | 28 -------------------------- ui/core/src/components/user/ApiKey.vue | 4 ++-- 6 files changed, 51 insertions(+), 46 deletions(-) delete mode 100644 sys/model/user_test.go diff --git a/go.mod b/go.mod index 43ab7c0e4..374ff2eeb 100644 --- a/go.mod +++ b/go.mod @@ -39,8 +39,8 @@ require ( go.etcd.io/bbolt v1.3.6 // indirect go.mongodb.org/mongo-driver v1.8.1 golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b - golang.org/x/net v0.0.0-20211209124913-491a49abca63 - golang.org/x/sys v0.0.0-20211210111614-af8b64212486 // indirect + golang.org/x/net v0.0.0-20211216030914-fe4d6282115f + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect gopkg.in/gavv/httpexpect.v2 v2.3.1 gopkg.in/sourcemap.v1 v1.0.5 // indirect ) @@ -53,6 +53,7 @@ require ( github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 // indirect github.com/fasthttp/websocket v1.4.3-rc.10 // indirect github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect + github.com/go-bindata/go-bindata/v3 v3.1.3 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect github.com/go-stack/stack v1.8.1 // indirect @@ -65,6 +66,7 @@ require ( github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/karrick/godirwalk v1.10.3 // indirect + github.com/kisielk/errcheck v1.6.0 // indirect github.com/klauspost/compress v1.13.6 // indirect github.com/labstack/echo v3.3.10+incompatible github.com/labstack/echo-contrib v0.11.0 @@ -85,6 +87,7 @@ require ( github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.31.0 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect + github.com/wadey/gocovmerge v0.0.0-20160331181800-b5bfa59ec0ad // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.0.2 // indirect github.com/xdg-go/stringprep v1.0.2 // indirect @@ -93,8 +96,10 @@ require ( github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect github.com/yudai/gojsondiff v1.0.0 // indirect github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect + github.com/yuin/goldmark v1.4.4 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect + golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.8 // indirect diff --git a/go.sum b/go.sum index 83e8d2c74..d5520fa7a 100644 --- a/go.sum +++ b/go.sum @@ -622,9 +622,11 @@ github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcm github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.4 h1:zNWRjYUW32G9KirMXYHQHVNFkXvMI7LpgNW2AgYAoIs= github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.0/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -729,8 +731,11 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f h1:hEYJvxw1lSnWIl8X9ofsYMklzaDs90JI2az5YMd4fPM= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -799,11 +804,15 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211209171907-798191bca915 h1:P+8mCzuEpyszAT6T42q0sxU+eveBAF/cJ2Kp0x6/8+0= golang.org/x/sys v0.0.0-20211209171907-798191bca915/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486 h1:5hpz5aRr+W1erYCL5JRhSUBJRph7l9XkNveoExlrKYk= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -854,6 +863,7 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w= golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/storage/database/user.go b/storage/database/user.go index be7b357ef..aa69a7131 100644 --- a/storage/database/user.go +++ b/storage/database/user.go @@ -428,8 +428,11 @@ func (me *UserDB) save(u *model.User, tx db.DB) error { func (me *UserDB) updateApiKeys(u *model.User, tx db.DB) error { newKeys := make([]model.ApiKey, 0) + var existingKeys []model.ApiKey + _ = tx.Get(userApiKeysBucket, u.ID, &existingKeys) + for _, a := range u.ApiKeys { - if a.IsNew() { + if me.keyIsNew(existingKeys, a) { newKeys = append(newKeys, *a) err := tx.Set(userApiKeyBucket, a.Key, u.ID) if err != nil { @@ -438,9 +441,8 @@ func (me *UserDB) updateApiKeys(u *model.User, tx db.DB) error { a.HideKey() } } + if len(newKeys) > 0 { - var existingKeys []model.ApiKey - _ = tx.Get(userApiKeysBucket, u.ID, &existingKeys) if len(existingKeys) > 0 { newKeys = append(newKeys, existingKeys...) } @@ -452,6 +454,20 @@ func (me *UserDB) updateApiKeys(u *model.User, tx db.DB) error { return nil } +func (me *UserDB) keyIsNew(existingKeys []model.ApiKey, apiKey *model.ApiKey) bool { + exists := false + var tmp model.ApiKey + for _, k := range existingKeys { + tmp.Key = apiKey.Key + tmp.HideKey() + if tmp.Key == k.Key { + exists = true + break + } + } + return !exists +} + func (me *UserDB) setTinyUserIconBase64(item *model.User) error { if item.PhotoPath == "" { return nil diff --git a/sys/model/apikey.go b/sys/model/apikey.go index 466059a6b..146baeb1c 100644 --- a/sys/model/apikey.go +++ b/sys/model/apikey.go @@ -5,6 +5,7 @@ import ( "strings" uuid "github.com/satori/go.uuid" + "golang.org/x/crypto/bcrypt" ) const ApiKeyLength = 40 //example: f235122f9a1e4884123456788a2126f8dd76996b @@ -24,20 +25,21 @@ func NewApiKey(name, uid string) (*ApiKey, error) { return &ApiKey{Name: name, Key: k[0:16] + uid[0:8] + k[16:]}, nil } -//HideKey turns f235122f9a1e4884123456788a2126f8dd76996b into f235...996b +//HideKey hashes the key func (me *ApiKey) HideKey() { - if me.IsNew() { - me.Key = me.Key[0:4] + "..." + me.Key[len(me.Key)-4:] - } + me.Key, _ = hashString(me.Key) } -//IsNew returns true on keys like f235122f9a1e4884123456788a2126f8dd76996b -//but hidden keys like f235...996b are hidden and therefore not new anymore -func (me *ApiKey) IsNew() bool { - //if not hidden then new - return len(me.Key) > 11 +func MatchesApiKey(hiddenApiKey, apiKey string) bool { + return checkStringHash(apiKey, hiddenApiKey) } -func MatchesApiKey(hiddenApiKey, apiKey string) bool { - return strings.HasPrefix(apiKey, hiddenApiKey[0:4]) && strings.HasSuffix(apiKey, hiddenApiKey[len(hiddenApiKey)-4:]) +func hashString(str string) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(str), 14) + return string(bytes), err +} + +func checkStringHash(str, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(str)) + return err == nil } diff --git a/sys/model/user_test.go b/sys/model/user_test.go deleted file mode 100644 index 7e80add90..000000000 --- a/sys/model/user_test.go +++ /dev/null @@ -1,28 +0,0 @@ -package model - -import ( - "testing" -) - -func TestUser_HideApiKeys(t *testing.T) { - u := &User{ID: "123456789010"} - _, err := u.NewApiKey("key1") - if err != nil { - t.Error(err) - return - } - - _, err = u.NewApiKey("key2") - if err != nil { - t.Error(err) - return - } - for _, a := range u.ApiKeys { - a.HideKey() - } - for _, a := range u.ApiKeys { - if len(a.Key) != 11 { - t.Error("wrong length! key not hidden?") - } - } -} diff --git a/ui/core/src/components/user/ApiKey.vue b/ui/core/src/components/user/ApiKey.vue index 6bc06321c..2886736b6 100644 --- a/ui/core/src/components/user/ApiKey.vue +++ b/ui/core/src/components/user/ApiKey.vue @@ -41,9 +41,9 @@ {{item.Name}} - + @@ -27,7 +27,7 @@ {{result.Name}} {{result.Key}}

- {{$t('Please copy this API key now as it will be not fully readable afterwards.')}} + {{$t('Please copy this API key now, as it will not be shown again.')}}

@@ -41,9 +41,6 @@ {{item.Name}} -