From 562ed2e0150331aff4937a456ee6904bfc2c676f Mon Sep 17 00:00:00 2001 From: ari melody Date: Tue, 29 Apr 2025 16:31:39 +0100 Subject: [PATCH] session validation/invalidation --- admin/http.go | 2 +- api/release.go | 2 +- controller/session.go | 19 ++++++++++++++++--- view/music.go | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/admin/http.go b/admin/http.go index 4d09264..bdacad3 100644 --- a/admin/http.go +++ b/admin/http.go @@ -483,7 +483,7 @@ func staticHandler() http.Handler { func enforceSession(app *model.AppState, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - session, err := controller.GetSessionFromRequest(app.DB, r) + session, err := controller.GetSessionFromRequest(app, r) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve session: %v\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) diff --git a/api/release.go b/api/release.go index efed8dd..5cb87b0 100644 --- a/api/release.go +++ b/api/release.go @@ -20,7 +20,7 @@ func ServeRelease(app *model.AppState, release *model.Release) http.Handler { // only allow authorised users to view hidden releases privileged := false if !release.Visible { - session, err := controller.GetSessionFromRequest(app.DB, r) + session, err := controller.GetSessionFromRequest(app, r) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve session: %v\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) diff --git a/controller/session.go b/controller/session.go index cf423fe..dce7ad0 100644 --- a/controller/session.go +++ b/controller/session.go @@ -8,6 +8,7 @@ import ( "strings" "time" + "arimelody-web/log" "arimelody-web/model" "github.com/jmoiron/sqlx" @@ -15,7 +16,7 @@ import ( const TOKEN_LEN = 64 -func GetSessionFromRequest(db *sqlx.DB, r *http.Request) (*model.Session, error) { +func GetSessionFromRequest(app *model.AppState, r *http.Request) (*model.Session, error) { sessionCookie, err := r.Cookie(model.COOKIE_TOKEN) if err != nil && err != http.ErrNoCookie { return nil, errors.New(fmt.Sprintf("Failed to retrieve session cookie: %v", err)) @@ -25,14 +26,26 @@ func GetSessionFromRequest(db *sqlx.DB, r *http.Request) (*model.Session, error) if sessionCookie != nil { // fetch existing session - session, err = GetSession(db, sessionCookie.Value) + session, err = GetSession(app.DB, sessionCookie.Value) if err != nil && !strings.Contains(err.Error(), "no rows") { return nil, errors.New(fmt.Sprintf("Failed to retrieve session: %v", err)) } if session != nil { - // TODO: consider running security checks here (i.e. user agent mismatches) + if session.UserAgent != r.UserAgent() { + msg := "Session user agent mismatch. A cookie may have been hijacked!" + if session.Account != nil { + account, _ := GetAccountByID(app.DB, session.Account.ID) + msg += " (Account \"" + account.Username + "\")" + } + app.Log.Warn(log.TYPE_ACCOUNT, msg) + err = DeleteSession(app.DB, session.Token) + if err != nil { + app.Log.Warn(log.TYPE_ACCOUNT, "Failed to delete affected session") + } + return nil, nil + } } } diff --git a/view/music.go b/view/music.go index 2d40ef0..dfe884e 100644 --- a/view/music.go +++ b/view/music.go @@ -60,7 +60,7 @@ func ServeGateway(app *model.AppState, release *model.Release) http.Handler { // only allow authorised users to view hidden releases privileged := false if !release.Visible { - session, err := controller.GetSessionFromRequest(app.DB, r) + session, err := controller.GetSessionFromRequest(app, r) if err != nil { fmt.Fprintf(os.Stderr, "WARN: Failed to retrieve session: %v\n", err) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)