Compare commits

...
Author SHA1 Message Date
Stavros 5c2cb08a7a fix: use constant time in user lookups 2026-07-14 15:14:26 +03:00
+29 -5
View File
@@ -89,21 +89,30 @@ func (controller *UserController) loginHandler(c *gin.Context) {
search, err := controller.auth.SearchUser(req.Username) search, err := controller.auth.SearchUser(req.Username)
if err != nil { if err != nil {
controller.constantTime(func() constantTimeRes {
if errors.Is(err, service.ErrUserNotFound) { if errors.Is(err, service.ErrUserNotFound) {
controller.log.App.Warn().Str("username", req.Username).Msg("User not found during login attempt") controller.log.App.Warn().Str("username", req.Username).Msg("User not found during login attempt")
controller.auth.RecordLoginAttempt(req.Username, false) controller.auth.RecordLoginAttempt(req.Username, false)
controller.log.AuditLoginFailure(req.Username, "unknown", c.ClientIP(), "user not found") controller.log.AuditLoginFailure(req.Username, "unknown", c.ClientIP(), "user not found")
c.JSON(401, gin.H{ return constantTimeRes{
Code: 401,
Res: gin.H{
"status": 401, "status": 401,
"message": "Unauthorized", "message": "Unauthorized",
}) },
return }
} }
controller.log.App.Error().Err(err).Str("username", req.Username).Msg("Error searching for user during login attempt") controller.log.App.Error().Err(err).Str("username", req.Username).Msg("Error searching for user during login attempt")
c.JSON(500, gin.H{ return constantTimeRes{
Code: 500,
Res: gin.H{
"status": 500, "status": 500,
"message": "Internal Server Error", "message": "Internal Server Error",
}) },
}
}, func(res constantTimeRes) {
c.JSON(res.Code, res.Res)
}, time.Millisecond*45)
return return
} }
@@ -466,3 +475,18 @@ func (controller *UserController) tailscaleHandler(c *gin.Context) {
"message": "Login successful", "message": "Login successful",
}) })
} }
type constantTimeRes struct {
Code int
Res any
}
func (controller *UserController) constantTime(f func() constantTimeRes, rf func(res constantTimeRes), targetTime time.Duration) {
tStart := time.Now()
res := f()
tEnd := time.Now()
if tEnd.Sub(tStart) < targetTime {
time.Sleep(targetTime - tEnd.Sub(tStart))
}
rf(res)
}