From 301bcaf5c40a299cb5974049a6b3bd08a1034738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20Markovi=C4=87?= Date: Sun, 7 Jun 2026 10:16:50 +0200 Subject: [PATCH] =?UTF-8?q?Bezbednost:=20re=C5=A1eno=207=20kriti=C4=8Dnih?= =?UTF-8?q?=20nalaza=20(HP-01=20do=20HP-07)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/ntech/main.go | 16 ++++------------ internal/db/sqlite/korisnici.go | 4 ++-- internal/handler/podesavanja.go | 7 +++---- internal/handler/prijava.go | 2 ++ internal/middleware/bezbednost.go | 2 +- internal/middleware/csrf.go | 2 ++ internal/middleware/flash.go | 22 ++++++++++++++++++---- 7 files changed, 32 insertions(+), 23 deletions(-) diff --git a/cmd/ntech/main.go b/cmd/ntech/main.go index ea8f8d1..602c1a6 100644 --- a/cmd/ntech/main.go +++ b/cmd/ntech/main.go @@ -3,7 +3,6 @@ package main import ( "context" "fmt" - "io" "io/fs" "log" "net/http" @@ -264,22 +263,15 @@ func napraviStartupBackup(putanjaBaze string) { ime := fmt.Sprintf("ntech_%s.db", time.Now().Format("20060102_150405")) odrediste := filepath.Join(folder, ime) - src, err := os.Open(putanjaBaze) + db, err := sqlite.OtvoriDB(putanjaBaze) if err != nil { log.Printf("backup: ne mogu otvoriti bazu: %v", err) return } - defer src.Close() + defer db.Close() - dst, err := os.Create(odrediste) - if err != nil { - log.Printf("backup: ne mogu kreirati backup fajl: %v", err) - return - } - defer dst.Close() - - if _, err := io.Copy(dst, src); err != nil { - log.Printf("backup: greška pri kopiranju: %v", err) + if _, err := db.ExecContext(context.Background(), "VACUUM INTO ?", odrediste); err != nil { + log.Printf("backup: greška pri pravljenju backup-a: %v", err) return } diff --git a/internal/db/sqlite/korisnici.go b/internal/db/sqlite/korisnici.go index 8edc9e6..9ccead6 100644 --- a/internal/db/sqlite/korisnici.go +++ b/internal/db/sqlite/korisnici.go @@ -94,7 +94,7 @@ func (r *sqliteKorisniciRepo) DohvatiPoID(ctx context.Context, id int64) (*model func (r *sqliteKorisniciRepo) Lista(ctx context.Context) ([]model.Korisnik, error) { rows, err := r.db.QueryContext(ctx, - `SELECT id, korisnicko_ime, lozinka_hash, uloga, aktivan, COALESCE(totp_tajna, ''), + `SELECT id, korisnicko_ime, uloga, aktivan, COALESCE(totp_tajna, ''), COALESCE(lokalna_tema, ''), koristi_lokalnu_temu, datum_kreiranja, COALESCE(lokalna_pozadina, ''), COALESCE(lokalna_pozadina_opacity, '50'), COALESCE(lokalna_pozadina_blur, '12'), COALESCE(lokalna_pozadina_blur_pozadine, '0'), @@ -111,7 +111,7 @@ func (r *sqliteKorisniciRepo) Lista(ctx context.Context) ([]model.Korisnik, erro var lokalnaTema sql.NullString var lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur, lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString var datumKreiranja time.Time - if err := rows.Scan(&k.ID, &k.KorisnickoIme, &k.LozinkaHash, &k.Uloga, &aktivan, &k.TotpTajna, + if err := rows.Scan(&k.ID, &k.KorisnickoIme, &k.Uloga, &aktivan, &k.TotpTajna, &lokalnaTema, &koristiLokalnuTemu, &datumKreiranja, &lokalnaPozadina, &lokalnaPozadinaOpacity, &lokalnaPozadinaBlur, &lokalnaPozadinaBlurPozadine, &lokalnaPozadinaGlassOpacity); err != nil { diff --git a/internal/handler/podesavanja.go b/internal/handler/podesavanja.go index d07fa5f..30ee89e 100644 --- a/internal/handler/podesavanja.go +++ b/internal/handler/podesavanja.go @@ -253,11 +253,10 @@ func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) { } sledeci := r.FormValue("_next") - if sledeci == "" { - http.Redirect(w, r, "/podesavanja?sacuvano=1", http.StatusSeeOther) - } else { - http.Redirect(w, r, sledeci+"?sacuvano=1", http.StatusSeeOther) + if sledeci == "" || !strings.HasPrefix(sledeci, "/") { + sledeci = "/podesavanja" } + http.Redirect(w, r, sledeci+"?sacuvano=1", http.StatusSeeOther) } // BackupBaze kreira konzistentnu kopiju baze i šalje je kao attachment diff --git a/internal/handler/prijava.go b/internal/handler/prijava.go index 27e402f..60681ee 100644 --- a/internal/handler/prijava.go +++ b/internal/handler/prijava.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/http" + "os" "strings" "time" @@ -304,6 +305,7 @@ func napraviKolacic(token string, istice time.Time) *http.Cookie { Path: "/", Expires: istice, HttpOnly: true, + Secure: os.Getenv("NTECH_ENV") == "production", SameSite: http.SameSiteStrictMode, } } diff --git a/internal/middleware/bezbednost.go b/internal/middleware/bezbednost.go index ed7acb2..d27b275 100644 --- a/internal/middleware/bezbednost.go +++ b/internal/middleware/bezbednost.go @@ -14,7 +14,7 @@ func BezbednostHeaders() func(http.Handler) http.Handler { h.Set("Content-Security-Policy", "default-src 'self'; "+ "style-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com; "+ - "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.tailwindcss.com https://cdn.jsdelivr.net; "+ + "script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com https://cdn.jsdelivr.net; "+ "img-src 'self' data: blob:; "+ "font-src 'self'; "+ "connect-src 'self'") diff --git a/internal/middleware/csrf.go b/internal/middleware/csrf.go index 0cd5b3d..ed5916c 100644 --- a/internal/middleware/csrf.go +++ b/internal/middleware/csrf.go @@ -5,6 +5,7 @@ import ( "crypto/rand" "encoding/base64" "net/http" + "os" ) const csrfKolacic = "ntech_csrf" @@ -38,6 +39,7 @@ func CsrfMiddleware(next http.Handler) http.Handler { Path: "/", MaxAge: 86400 * 30, HttpOnly: true, + Secure: os.Getenv("NTECH_ENV") == "production", SameSite: http.SameSiteStrictMode, }) } diff --git a/internal/middleware/flash.go b/internal/middleware/flash.go index c583a69..5421c43 100644 --- a/internal/middleware/flash.go +++ b/internal/middleware/flash.go @@ -29,17 +29,31 @@ func GetFlash(r *http.Request, db *sql.DB) *model.FlashPoruka { if err != nil { return nil } + + tx, err := db.BeginTx(r.Context(), nil) + if err != nil { + return nil + } + defer tx.Rollback() + var flashJSON sql.NullString - if err := db.QueryRowContext(r.Context(), + if err := tx.QueryRowContext(r.Context(), `SELECT flash FROM sesije WHERE token = ?`, kolacic.Value).Scan(&flashJSON); err != nil { return nil } if !flashJSON.Valid || flashJSON.String == "" { return nil } - // briše pre parsiranja — ako parsiranje ne uspe, poruka se svakako ne prikazuje - db.ExecContext(r.Context(), - `UPDATE sesije SET flash = NULL WHERE token = ?`, kolacic.Value) + + if _, err := tx.ExecContext(r.Context(), + `UPDATE sesije SET flash = NULL WHERE token = ?`, kolacic.Value); err != nil { + return nil + } + + if err := tx.Commit(); err != nil { + return nil + } + var f model.FlashPoruka if err := json.Unmarshal([]byte(flashJSON.String), &f); err != nil { return nil