From ff86222fc9ab85fb4c5c5e8a063083595b323761 Mon Sep 17 00:00:00 2001 From: GrailFinder Date: Sun, 5 May 2024 08:57:23 +0300 Subject: Enha: protected cookies --- internal/handlers/auth.go | 23 +++++++++++++++++++---- internal/handlers/middleware.go | 37 ++++++++++++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 9 deletions(-) (limited to 'internal/handlers') diff --git a/internal/handlers/auth.go b/internal/handlers/auth.go index 5ec1c80..e7eca50 100644 --- a/internal/handlers/auth.go +++ b/internal/handlers/auth.go @@ -3,6 +3,9 @@ package handlers import ( "apjournal/internal/models" "apjournal/pkg/utils" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" "encoding/json" "html/template" "net/http" @@ -61,6 +64,7 @@ func (h *Handlers) HandleLogin(w http.ResponseWriter, r *http.Request) { } func (h *Handlers) makeCookie(username string, remote string) (*http.Cookie, error) { + // secret // Create a new random session token // sessionToken := xid.New().String() sessionToken := "token" @@ -70,10 +74,18 @@ func (h *Handlers) makeCookie(username string, remote string) (*http.Cookie, err Username: username, Expiry: expiresAt, } - // TODO: write session to db + cookieName := "session_token" + // hmac to protect cookies + hm := hmac.New(sha256.New, []byte(h.cfg.CookieSecret)) + hm.Write([]byte(cookieName)) + hm.Write([]byte(sessionToken)) + signature := hm.Sum(nil) + // b64 enc to avoid non-ascii + cookieValue := base64.URLEncoding.EncodeToString([]byte( + string(signature) + sessionToken)) cookie := &http.Cookie{ - Name: "session_token", - Value: sessionToken, + Name: cookieName, + Value: cookieValue, Secure: true, HttpOnly: true, SameSite: http.SameSiteNoneMode, @@ -86,7 +98,10 @@ func (h *Handlers) makeCookie(username string, remote string) (*http.Cookie, err cookie.Domain = "192.168.0.101" } // set ctx? - // c.Set("username", username) + // set user in session + if err := h.cacheSetSession(sessionToken, session); err != nil { + return nil, err + } return cookie, nil } diff --git a/internal/handlers/middleware.go b/internal/handlers/middleware.go index 28ccdbc..8b871a2 100644 --- a/internal/handlers/middleware.go +++ b/internal/handlers/middleware.go @@ -2,24 +2,51 @@ package handlers import ( "context" + "crypto/hmac" + "crypto/sha256" + "encoding/base64" "errors" "net/http" ) func (h *Handlers) GetSession(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - sessionCookie, err := r.Cookie("session_token") + cookieName := "session_token" + sessionCookie, err := r.Cookie(cookieName) if err != nil { msg := "auth failed; failed to get session token from cookies" h.log.Debug(msg, "error", err) next.ServeHTTP(w, r) return } - sessionToken := "" - if sessionCookie.Value == "" { - sessionToken = sessionCookie.Value + cookieValueB, err := base64.URLEncoding. + DecodeString(sessionCookie.Value) + if err != nil { + msg := "auth failed; failed to decode b64 cookie" + h.log.Debug(msg, "error", err) + next.ServeHTTP(w, r) + return + } + cookieValue := string(cookieValueB) + if len(cookieValue) < sha256.Size { + h.log.Warn("small cookie", "size", len(cookieValue)) + next.ServeHTTP(w, r) + return + } + // Split apart the signature and original cookie value. + signature := cookieValue[:sha256.Size] + sessionToken := cookieValue[sha256.Size:] + //verify signature + mac := hmac.New(sha256.New, []byte(h.cfg.CookieSecret)) + mac.Write([]byte(cookieName)) + mac.Write([]byte(sessionToken)) + expectedSignature := mac.Sum(nil) + if !hmac.Equal([]byte(signature), expectedSignature) { + h.log.Debug("cookie with an invalid sign") + next.ServeHTTP(w, r) + return } - userSession, err := h.cacheGetSession(sessionCookie.Value) + userSession, err := h.cacheGetSession(sessionToken) if err != nil { msg := "auth failed; session does not exists" err = errors.New(msg) -- cgit v1.2.3