Bezbednost: SSRF — proširena whitelist na privatne mreže (192.168.x, 10.x, 172.16-31.x)
Pre: samo 127.0.0.1 i localhost — nije radilo sa Fisk serverom na drugoj mašini u LAN-u Posle: jePrivatnaAdresa() dozvoljava sve privatne IPv4 opsege + localhost Hostname se koristi direktno (iz validiranog URL-a), port kao integer
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
|||||||
"html"
|
"html"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@@ -924,12 +925,12 @@ func (h *Handler) TestFiskalizacije(w http.ResponseWriter, r *http.Request) {
|
|||||||
pfrURL = vrednostIliDefault(podesavanja, "pfr_url", "http://127.0.0.1:4566")
|
pfrURL = vrednostIliDefault(podesavanja, "pfr_url", "http://127.0.0.1:4566")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SSRF zaštita: URL validiramo, izvlačimo SAMO port, a host hardkodiramo.
|
// SSRF zaštita: dozvoljeni su samo localhost i privatne mreže (192.168.x.x,
|
||||||
// Port konvertujemo u integer da presečemo CodeQL taint lanac — broj ne može
|
// 10.x.x.x, 172.16-31.x.x, 127.x.x.x). Port izvlačimo kao integer da presečemo
|
||||||
// da sadrži URL manipulaciju, pa rekonstruisani URL nije korisnički unos.
|
// CodeQL taint lanac — broj ne može da sadrži URL manipulaciju.
|
||||||
parsedURL, err := url.Parse(pfrURL)
|
parsedURL, err := url.Parse(pfrURL)
|
||||||
if err != nil || (parsedURL.Hostname() != "127.0.0.1" && parsedURL.Hostname() != "localhost") {
|
if err != nil || !jePrivatnaAdresa(parsedURL.Hostname()) {
|
||||||
http.Error(w, "Nevažeći PFR URL — dozvoljen samo localhost", http.StatusBadRequest)
|
http.Error(w, "Nevažeći PFR URL — dozvoljeni su samo lokalni/privatni hostovi (127.x, 192.168.x, 10.x, 172.16-31.x)", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
port := 4566
|
port := 4566
|
||||||
@@ -938,7 +939,7 @@ func (h *Handler) TestFiskalizacije(w http.ResponseWriter, r *http.Request) {
|
|||||||
port = n
|
port = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statusURL := fmt.Sprintf("http://127.0.0.1:%d/api/status", port)
|
statusURL := fmt.Sprintf("http://%s:%d/api/status", parsedURL.Hostname(), port)
|
||||||
|
|
||||||
klijent := &http.Client{Timeout: 5 * time.Second}
|
klijent := &http.Client{Timeout: 5 * time.Second}
|
||||||
resp, err := klijent.Get(statusURL)
|
resp, err := klijent.Get(statusURL)
|
||||||
@@ -967,6 +968,26 @@ func (h *Handler) TestFiskalizacije(w http.ResponseWriter, r *http.Request) {
|
|||||||
</div>`, html.EscapeString(pfrURL), html.EscapeString(tin), html.EscapeString(lastInvoice), html.EscapeString(sdcDateTime))
|
</div>`, html.EscapeString(pfrURL), html.EscapeString(tin), html.EscapeString(lastInvoice), html.EscapeString(sdcDateTime))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// jePrivatnaAdresa proverava da li je hostname u opsegu privatnih/lokalnih mreža.
|
||||||
|
// Dozvoljeni: 127.x.x.x, localhost, 10.x.x.x, 172.16-31.x.x, 192.168.x.x.
|
||||||
|
func jePrivatnaAdresa(hostname string) bool {
|
||||||
|
if hostname == "localhost" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ip := net.ParseIP(hostname)
|
||||||
|
if ip == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ip4 := ip.To4()
|
||||||
|
if ip4 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return ip4[0] == 127 || // loopback
|
||||||
|
ip4[0] == 10 || // privatna klasa A
|
||||||
|
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // privatna klasa B
|
||||||
|
(ip4[0] == 192 && ip4[1] == 168) // privatna klasa C
|
||||||
|
}
|
||||||
|
|
||||||
// PodesavanjaSistem renderuje stranicu sa sistemskim podešavanjima (backup)
|
// PodesavanjaSistem renderuje stranicu sa sistemskim podešavanjima (backup)
|
||||||
func (h *Handler) PodesavanjaSistem(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) PodesavanjaSistem(w http.ResponseWriter, r *http.Request) {
|
||||||
if _, ok := h.zahtevajDozvolu(w, r, "podesavanja.pregled"); !ok {
|
if _, ok := h.zahtevajDozvolu(w, r, "podesavanja.pregled"); !ok {
|
||||||
|
|||||||
Reference in New Issue
Block a user