Skip to content

Commit e5fbe72

Browse files
authored
fix(security): add login count validation for webdav (#693)
1 parent 283f372 commit e5fbe72

File tree

4 files changed

+39
-21
lines changed

4 files changed

+39
-21
lines changed

internal/model/user.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/OpenListTeam/OpenList/v4/internal/errs"
1010
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
1111
"github.com/OpenListTeam/OpenList/v4/pkg/utils/random"
12+
"github.com/OpenListTeam/go-cache"
1213
"github.com/go-webauthn/webauthn/webauthn"
1314
"github.com/pkg/errors"
1415
)
@@ -21,6 +22,13 @@ const (
2122

2223
const StaticHashSalt = "https://github.com/alist-org/alist"
2324

25+
var LoginCache = cache.NewMemCache[int]()
26+
27+
var (
28+
DefaultLockDuration = time.Minute * 5
29+
DefaultMaxAuthRetries = 5
30+
)
31+
2432
type User struct {
2533
ID uint `json:"id" gorm:"primaryKey"` // unique key
2634
Username string `json:"username" gorm:"unique" binding:"required"` // username

server/handles/auth.go

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,14 @@ import (
44
"bytes"
55
"encoding/base64"
66
"image/png"
7-
"time"
87

98
"github.com/OpenListTeam/OpenList/v4/internal/model"
109
"github.com/OpenListTeam/OpenList/v4/internal/op"
1110
"github.com/OpenListTeam/OpenList/v4/server/common"
12-
"github.com/OpenListTeam/go-cache"
1311
"github.com/gin-gonic/gin"
1412
"github.com/pquerna/otp/totp"
1513
)
1614

17-
var loginCache = cache.NewMemCache[int]()
18-
var (
19-
defaultDuration = time.Minute * 5
20-
defaultTimes = 5
21-
)
22-
2315
type LoginReq struct {
2416
Username string `json:"username" binding:"required"`
2517
Password string `json:"password"`
@@ -50,30 +42,30 @@ func LoginHash(c *gin.Context) {
5042
func loginHash(c *gin.Context, req *LoginReq) {
5143
// check count of login
5244
ip := c.ClientIP()
53-
count, ok := loginCache.Get(ip)
54-
if ok && count >= defaultTimes {
45+
count, ok := model.LoginCache.Get(ip)
46+
if ok && count >= model.DefaultMaxAuthRetries {
5547
common.ErrorStrResp(c, "Too many unsuccessful sign-in attempts have been made using an incorrect username or password, Try again later.", 429)
56-
loginCache.Expire(ip, defaultDuration)
48+
model.LoginCache.Expire(ip, model.DefaultLockDuration)
5749
return
5850
}
5951
// check username
6052
user, err := op.GetUserByName(req.Username)
6153
if err != nil {
6254
common.ErrorResp(c, err, 400)
63-
loginCache.Set(ip, count+1)
55+
model.LoginCache.Set(ip, count+1)
6456
return
6557
}
6658
// validate password hash
6759
if err := user.ValidatePwdStaticHash(req.Password); err != nil {
6860
common.ErrorResp(c, err, 400)
69-
loginCache.Set(ip, count+1)
61+
model.LoginCache.Set(ip, count+1)
7062
return
7163
}
7264
// check 2FA
7365
if user.OtpSecret != "" {
7466
if !totp.Validate(req.OtpCode, user.OtpSecret) {
7567
common.ErrorStrResp(c, "Invalid 2FA code", 402)
76-
loginCache.Set(ip, count+1)
68+
model.LoginCache.Set(ip, count+1)
7769
return
7870
}
7971
}
@@ -84,7 +76,7 @@ func loginHash(c *gin.Context, req *LoginReq) {
8476
return
8577
}
8678
common.SuccessResp(c, gin.H{"token": token})
87-
loginCache.Del(ip)
79+
model.LoginCache.Del(ip)
8880
}
8981

9082
type UserResp struct {

server/handles/ldap_login.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ func loginLdap(c *gin.Context, req *LoginReq) {
3636

3737
// check count of login
3838
ip := c.ClientIP()
39-
count, ok := loginCache.Get(ip)
40-
if ok && count >= defaultTimes {
39+
count, ok := model.LoginCache.Get(ip)
40+
if ok && count >= model.DefaultMaxAuthRetries {
4141
common.ErrorStrResp(c, "Too many unsuccessful sign-in attempts have been made using an incorrect username or password, Try again later.", 429)
42-
loginCache.Expire(ip, defaultDuration)
42+
model.LoginCache.Expire(ip, model.DefaultLockDuration)
4343
return
4444
}
4545

@@ -94,7 +94,7 @@ func loginLdap(c *gin.Context, req *LoginReq) {
9494
if err != nil {
9595
utils.Log.Errorf("Failed to auth. %v", err)
9696
common.ErrorResp(c, err, 400)
97-
loginCache.Set(ip, count+1)
97+
model.LoginCache.Set(ip, count+1)
9898
return
9999
} else {
100100
utils.Log.Infof("Auth successful username:%s", req.Username)
@@ -106,7 +106,7 @@ func loginLdap(c *gin.Context, req *LoginReq) {
106106
user, err = ladpRegister(req.Username)
107107
if err != nil {
108108
common.ErrorResp(c, err, 400)
109-
loginCache.Set(ip, count+1)
109+
model.LoginCache.Set(ip, count+1)
110110
return
111111
}
112112
}
@@ -118,7 +118,7 @@ func loginLdap(c *gin.Context, req *LoginReq) {
118118
return
119119
}
120120
common.SuccessResp(c, gin.H{"token": token})
121-
loginCache.Del(ip)
121+
model.LoginCache.Del(ip)
122122
}
123123

124124
func ladpRegister(username string) (*model.User, error) {

server/webdav.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"path"
77
"strings"
88

9+
"github.com/OpenListTeam/OpenList/v4/internal/model"
910
"github.com/OpenListTeam/OpenList/v4/internal/stream"
1011
"github.com/OpenListTeam/OpenList/v4/server/middlewares"
1112

@@ -47,7 +48,21 @@ func ServeWebDAV(c *gin.Context) {
4748
}
4849

4950
func WebDAVAuth(c *gin.Context) {
51+
// check count of login
52+
ip := c.ClientIP()
5053
guest, _ := op.GetGuest()
54+
count, cok := model.LoginCache.Get(ip)
55+
if cok && count >= model.DefaultMaxAuthRetries {
56+
if c.Request.Method == "OPTIONS" {
57+
c.Set("user", guest)
58+
c.Next()
59+
return
60+
}
61+
c.Status(http.StatusTooManyRequests)
62+
c.Abort()
63+
model.LoginCache.Expire(ip, model.DefaultLockDuration)
64+
return
65+
}
5166
username, password, ok := c.Request.BasicAuth()
5267
if !ok {
5368
bt := c.GetHeader("Authorization")
@@ -85,10 +100,13 @@ func WebDAVAuth(c *gin.Context) {
85100
c.Next()
86101
return
87102
}
103+
model.LoginCache.Set(ip, count+1)
88104
c.Status(http.StatusUnauthorized)
89105
c.Abort()
90106
return
91107
}
108+
// at least auth is successful till here
109+
model.LoginCache.Del(ip)
92110
if user.Disabled || !user.CanWebdavRead() {
93111
if c.Request.Method == "OPTIONS" {
94112
c.Set("user", guest)

0 commit comments

Comments
 (0)