Bezbednost: rešeno 7 kritičnih nalaza (HP-01 do HP-07)
This commit is contained in:
+4
-12
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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'")
|
||||||
|
|||||||
@@ -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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user