Bezbednost: rešeno 7 kritičnih nalaza (HP-01 do HP-07)

This commit is contained in:
2026-06-07 10:16:50 +02:00
parent df8c357566
commit 301bcaf5c4
7 changed files with 32 additions and 23 deletions
+4 -12
View File
@@ -3,7 +3,6 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"io"
"io/fs" "io/fs"
"log" "log"
"net/http" "net/http"
@@ -264,22 +263,15 @@ func napraviStartupBackup(putanjaBaze string) {
ime := fmt.Sprintf("ntech_%s.db", time.Now().Format("20060102_150405")) ime := fmt.Sprintf("ntech_%s.db", time.Now().Format("20060102_150405"))
odrediste := filepath.Join(folder, ime) odrediste := filepath.Join(folder, ime)
src, err := os.Open(putanjaBaze) db, err := sqlite.OtvoriDB(putanjaBaze)
if err != nil { if err != nil {
log.Printf("backup: ne mogu otvoriti bazu: %v", err) log.Printf("backup: ne mogu otvoriti bazu: %v", err)
return return
} }
defer src.Close() defer db.Close()
dst, err := os.Create(odrediste) if _, err := db.ExecContext(context.Background(), "VACUUM INTO ?", odrediste); err != nil {
if err != nil { log.Printf("backup: greška pri pravljenju backup-a: %v", err)
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)
return return
} }
+2 -2
View File
@@ -94,7 +94,7 @@ func (r *sqliteKorisniciRepo) DohvatiPoID(ctx context.Context, id int64) (*model
func (r *sqliteKorisniciRepo) Lista(ctx context.Context) ([]model.Korisnik, error) { func (r *sqliteKorisniciRepo) Lista(ctx context.Context) ([]model.Korisnik, error) {
rows, err := r.db.QueryContext(ctx, 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_tema, ''), koristi_lokalnu_temu, datum_kreiranja,
COALESCE(lokalna_pozadina, ''), COALESCE(lokalna_pozadina_opacity, '50'), COALESCE(lokalna_pozadina, ''), COALESCE(lokalna_pozadina_opacity, '50'),
COALESCE(lokalna_pozadina_blur, '12'), COALESCE(lokalna_pozadina_blur_pozadine, '0'), 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 lokalnaTema sql.NullString
var lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur, lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString var lokalnaPozadina, lokalnaPozadinaOpacity, lokalnaPozadinaBlur, lokalnaPozadinaBlurPozadine, lokalnaPozadinaGlassOpacity sql.NullString
var datumKreiranja time.Time 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, &lokalnaTema, &koristiLokalnuTemu, &datumKreiranja,
&lokalnaPozadina, &lokalnaPozadinaOpacity, &lokalnaPozadinaBlur, &lokalnaPozadinaBlurPozadine, &lokalnaPozadina, &lokalnaPozadinaOpacity, &lokalnaPozadinaBlur, &lokalnaPozadinaBlurPozadine,
&lokalnaPozadinaGlassOpacity); err != nil { &lokalnaPozadinaGlassOpacity); err != nil {
+3 -4
View File
@@ -253,11 +253,10 @@ func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) {
} }
sledeci := r.FormValue("_next") sledeci := r.FormValue("_next")
if sledeci == "" { if sledeci == "" || !strings.HasPrefix(sledeci, "/") {
http.Redirect(w, r, "/podesavanja?sacuvano=1", http.StatusSeeOther) sledeci = "/podesavanja"
} else {
http.Redirect(w, r, sledeci+"?sacuvano=1", http.StatusSeeOther)
} }
http.Redirect(w, r, sledeci+"?sacuvano=1", http.StatusSeeOther)
} }
// BackupBaze kreira konzistentnu kopiju baze i šalje je kao attachment // BackupBaze kreira konzistentnu kopiju baze i šalje je kao attachment
+2
View File
@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"net" "net"
"net/http" "net/http"
"os"
"strings" "strings"
"time" "time"
@@ -304,6 +305,7 @@ func napraviKolacic(token string, istice time.Time) *http.Cookie {
Path: "/", Path: "/",
Expires: istice, Expires: istice,
HttpOnly: true, HttpOnly: true,
Secure: os.Getenv("NTECH_ENV") == "production",
SameSite: http.SameSiteStrictMode, SameSite: http.SameSiteStrictMode,
} }
} }
+1 -1
View File
@@ -14,7 +14,7 @@ func BezbednostHeaders() func(http.Handler) http.Handler {
h.Set("Content-Security-Policy", h.Set("Content-Security-Policy",
"default-src 'self'; "+ "default-src 'self'; "+
"style-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com; "+ "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:; "+ "img-src 'self' data: blob:; "+
"font-src 'self'; "+ "font-src 'self'; "+
"connect-src 'self'") "connect-src 'self'")
+2
View File
@@ -5,6 +5,7 @@ import (
"crypto/rand" "crypto/rand"
"encoding/base64" "encoding/base64"
"net/http" "net/http"
"os"
) )
const csrfKolacic = "ntech_csrf" const csrfKolacic = "ntech_csrf"
@@ -38,6 +39,7 @@ func CsrfMiddleware(next http.Handler) http.Handler {
Path: "/", Path: "/",
MaxAge: 86400 * 30, MaxAge: 86400 * 30,
HttpOnly: true, HttpOnly: true,
Secure: os.Getenv("NTECH_ENV") == "production",
SameSite: http.SameSiteStrictMode, SameSite: http.SameSiteStrictMode,
}) })
} }
+18 -4
View File
@@ -29,17 +29,31 @@ func GetFlash(r *http.Request, db *sql.DB) *model.FlashPoruka {
if err != nil { if err != nil {
return nil return nil
} }
tx, err := db.BeginTx(r.Context(), nil)
if err != nil {
return nil
}
defer tx.Rollback()
var flashJSON sql.NullString 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 { `SELECT flash FROM sesije WHERE token = ?`, kolacic.Value).Scan(&flashJSON); err != nil {
return nil return nil
} }
if !flashJSON.Valid || flashJSON.String == "" { if !flashJSON.Valid || flashJSON.String == "" {
return nil return nil
} }
// briše pre parsiranja — ako parsiranje ne uspe, poruka se svakako ne prikazuje
db.ExecContext(r.Context(), if _, err := tx.ExecContext(r.Context(),
`UPDATE sesije SET flash = NULL WHERE token = ?`, kolacic.Value) `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 var f model.FlashPoruka
if err := json.Unmarshal([]byte(flashJSON.String), &f); err != nil { if err := json.Unmarshal([]byte(flashJSON.String), &f); err != nil {
return nil return nil