From 532f95848c4b4dac7e337076a7d096a963392663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20Markovi=C4=87?= Date: Tue, 16 Jun 2026 03:32:07 +0200 Subject: [PATCH] =?UTF-8?q?Bezbednost:=20open=20redirect=20i=20kola=C4=8Di?= =?UTF-8?q?=C4=87i=20bez=20Secure=20atributa?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _next parametar: sanitizacija preko url.Parse (Host+Scheme prazan = relativan URL) umesto ručnog string check-a koji CodeQL nije prepoznavao - Kolačići: dodat Secure atribut (true u produkciji, false u razvoju) na 4 mesta: ntech_sesija brisanje (auth.go, prijava.go), ntech_flash_greska postavljanje i brisanje (auth.go, dashboard.go) --- internal/handler/dashboard.go | 11 +++++++---- internal/handler/podesavanja.go | 9 +++++++-- internal/handler/prijava.go | 12 +++++++----- internal/middleware/auth.go | 14 +++++++++----- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/internal/handler/dashboard.go b/internal/handler/dashboard.go index 30acb91..5db3152 100644 --- a/internal/handler/dashboard.go +++ b/internal/handler/dashboard.go @@ -3,6 +3,7 @@ package handler import ( "log/slog" "net/http" + "os" appdb "ntech/internal/db" "ntech/internal/db/sqlite" @@ -19,10 +20,12 @@ func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) { if kol, err := r.Cookie("ntech_flash_greska"); err == nil { flashGreska = kol.Value http.SetCookie(w, &http.Cookie{ - Name: "ntech_flash_greska", - Value: "", - Path: "/", - MaxAge: -1, + Name: "ntech_flash_greska", + Value: "", + Path: "/", + MaxAge: -1, + HttpOnly: true, + Secure: os.Getenv("NTECH_ENV") == "production", }) } diff --git a/internal/handler/podesavanja.go b/internal/handler/podesavanja.go index 43118ac..d5985c4 100644 --- a/internal/handler/podesavanja.go +++ b/internal/handler/podesavanja.go @@ -7,6 +7,7 @@ import ( "io" "log/slog" "net/http" + "net/url" "os" "path/filepath" "regexp" @@ -264,8 +265,12 @@ func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) { } sledeci := "/podesavanja" - if next := r.FormValue("_next"); strings.HasPrefix(next, "/") && (len(next) == 1 || (next[1] != '/' && next[1] != '\\')) { - sledeci = next + if next := r.FormValue("_next"); next != "" { + if u, err := url.Parse(next); err == nil && u.Host == "" && u.Scheme == "" { + if p := u.RequestURI(); p != "" { + sledeci = p + } + } } // backup podešavanja — pri neispravnom unosu javljamo jasnu grešku diff --git a/internal/handler/prijava.go b/internal/handler/prijava.go index 1a4b771..e028b27 100644 --- a/internal/handler/prijava.go +++ b/internal/handler/prijava.go @@ -262,11 +262,13 @@ func (h *Handler) Odjava(w http.ResponseWriter, r *http.Request) { _ = h.SesijeRepo.Obrisi(r.Context(), kolacic.Value) } http.SetCookie(w, &http.Cookie{ - Name: imeKolacica, - Value: "", - Path: "/", - Expires: time.Unix(0, 0), - MaxAge: -1, + Name: imeKolacica, + Value: "", + Path: "/", + Expires: time.Unix(0, 0), + MaxAge: -1, + Secure: os.Getenv("NTECH_ENV") == "production", + HttpOnly: true, }) http.Redirect(w, r, "/prijava", http.StatusSeeOther) } diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index bf84384..21ad350 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -5,6 +5,7 @@ import ( "database/sql" "errors" "net/http" + "os" "time" "ntech/internal/db/sqlite" @@ -34,11 +35,13 @@ func RequireAuth(db *sql.DB, totpKljuc []byte) func(http.Handler) http.Handler { if err != nil { // nevažeći token — briši kolačić i preusmeri http.SetCookie(w, &http.Cookie{ - Name: "ntech_sesija", - Value: "", - Path: "/", - Expires: time.Unix(0, 0), - MaxAge: -1, + Name: "ntech_sesija", + Value: "", + Path: "/", + Expires: time.Unix(0, 0), + MaxAge: -1, + Secure: os.Getenv("NTECH_ENV") == "production", + HttpOnly: true, }) http.Redirect(w, r, "/prijava", http.StatusSeeOther) return @@ -154,6 +157,7 @@ func postaviFlashGresku(w http.ResponseWriter, poruka string) { Path: "/", MaxAge: 60, HttpOnly: true, + Secure: os.Getenv("NTECH_ENV") == "production", }) }