From 5c2cb08a7a09bd983595270e9434e6d3266e6b57 Mon Sep 17 00:00:00 2001 From: Stavros Date: Tue, 14 Jul 2026 15:14:26 +0300 Subject: [PATCH] fix: use constant time in user lookups --- internal/controller/user_controller.go | 54 +++++++++++++++++++------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/internal/controller/user_controller.go b/internal/controller/user_controller.go index ae6c23bf..f5b6a5e1 100644 --- a/internal/controller/user_controller.go +++ b/internal/controller/user_controller.go @@ -89,21 +89,30 @@ func (controller *UserController) loginHandler(c *gin.Context) { search, err := controller.auth.SearchUser(req.Username) if err != nil { - if errors.Is(err, service.ErrUserNotFound) { - controller.log.App.Warn().Str("username", req.Username).Msg("User not found during login attempt") - controller.auth.RecordLoginAttempt(req.Username, false) - controller.log.AuditLoginFailure(req.Username, "unknown", c.ClientIP(), "user not found") - c.JSON(401, gin.H{ - "status": 401, - "message": "Unauthorized", - }) - return - } - controller.log.App.Error().Err(err).Str("username", req.Username).Msg("Error searching for user during login attempt") - c.JSON(500, gin.H{ - "status": 500, - "message": "Internal Server Error", - }) + controller.constantTime(func() constantTimeRes { + if errors.Is(err, service.ErrUserNotFound) { + controller.log.App.Warn().Str("username", req.Username).Msg("User not found during login attempt") + controller.auth.RecordLoginAttempt(req.Username, false) + controller.log.AuditLoginFailure(req.Username, "unknown", c.ClientIP(), "user not found") + return constantTimeRes{ + Code: 401, + Res: gin.H{ + "status": 401, + "message": "Unauthorized", + }, + } + } + controller.log.App.Error().Err(err).Str("username", req.Username).Msg("Error searching for user during login attempt") + return constantTimeRes{ + Code: 500, + Res: gin.H{ + "status": 500, + "message": "Internal Server Error", + }, + } + }, func(res constantTimeRes) { + c.JSON(res.Code, res.Res) + }, time.Millisecond*45) return } @@ -466,3 +475,18 @@ func (controller *UserController) tailscaleHandler(c *gin.Context) { "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) +}