package main import ( "embed" "flag" "fmt" "html/template" "io/fs" "log" "net" "net/http" "os" "sync" "time" ) //go:embed templates/* var templatesFS embed.FS //go:embed static/* var staticFS embed.FS // User struktura za uporabniške podatke type User struct { ID int Username string Password string Email string FullName string } // In-memory shranjevanje uporabnikov var ( users = make(map[string]*User) usersMux = &sync.RWMutex{} lastID = 0 ) // Template cache var templates *template.Template func init() { // Privzeti uporabniki addUser("jnovak", "secure123", "janez.novak@securebank.si", "Janez Novak") addUser("mpetric", "banking456", "maja.petric@securebank.si", "Maja Petrič") } func addUser(username, password, email, fullName string) { usersMux.Lock() defer usersMux.Unlock() lastID++ users[username] = &User{ ID: lastID, Username: username, Password: password, Email: email, FullName: fullName, } } func main() { // Nastavimo port preko command line argumenta var port = flag.String("port", "8080", "Port na katerem se bo zagnal server") var rateLimit = flag.Int("rate-limit", 3000, "Maksimalno število zahtev na sekundo") flag.Parse() // Naložimo HTML predloge iz embedded FS var err error templates, err = template.ParseFS(templatesFS, "templates/*.html") if err != nil { log.Fatal("Napaka pri nalaganju embedded predlog:", err) } // Create rate killer first killer := newRateKiller(*rateLimit) // Start network attack monitors with direct access to killer go monitorTCP(killer) go monitorUDP(killer) // Statične datoteke iz embedded FS staticSubFS, err := fs.Sub(staticFS, "static") if err != nil { log.Fatal("Napaka pri ustvarjanju static subFS:", err) } mux := http.NewServeMux() mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticSubFS)))) // Routes mux.HandleFunc("/", homeHandler) mux.HandleFunc("/register", registerHandler) mux.HandleFunc("/login", loginHandler) mux.HandleFunc("/dashboard", dashboardHandler) mux.HandleFunc("/logout", logoutHandler) // Zagon strežnika fmt.Printf("🏦 SecureBank aplikacija (embedded build)\n") fmt.Printf("Port: %s\n", *port) fmt.Printf("HTTP: http://localhost:%s\n", *port) fmt.Printf("Rate Limit: %d req/sec\n", *rateLimit) fmt.Printf("📦 Vsi fajli so vključeni v binary!\n") fmt.Println() serverAddr := ":" + *port log.Fatal(http.ListenAndServe(serverAddr, killer.wrap(mux))) } type rateKiller struct { limit int mu sync.Mutex window time.Time count int } func newRateKiller(limit int) *rateKiller { rk := &rateKiller{ limit: limit, window: time.Now().Truncate(time.Second), } // Reset counter every second go rk.resetCounter() return rk } func (rk *rateKiller) wrap(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { rk.mu.Lock() nowWindow := time.Now().Truncate(time.Second) if nowWindow.After(rk.window) { rk.window = nowWindow rk.count = 0 } rk.count++ exceeded := rk.count > rk.limit rk.mu.Unlock() if exceeded { log.Printf("CRITICAL: request rate exceeded %d req/s - shutting down", rk.limit) os.Exit(1) } next.ServeHTTP(w, r) }) } func (rk *rateKiller) recordPacket() { rk.mu.Lock() rk.count++ current := rk.count rk.mu.Unlock() if current > rk.limit { log.Printf("CRITICAL: attack detected - %d packets/sec exceeded limit of %d - SHUTTING DOWN", current, rk.limit) os.Exit(1) } } func (rk *rateKiller) resetCounter() { ticker := time.NewTicker(time.Second) defer ticker.Stop() for range ticker.C { rk.mu.Lock() if rk.count > 0 { log.Printf("📊 Rate: %d packets/sec (limit: %d)", rk.count, rk.limit) } rk.count = 0 rk.window = time.Now().Truncate(time.Second) rk.mu.Unlock() } } func monitorTCP(killer *rateKiller) { listener, err := net.Listen("tcp", ":80") if err != nil { log.Printf("Warning: TCP monitor on :80 failed: %v", err) return } defer listener.Close() log.Println("🔍 TCP attack monitor active on :80") for { conn, err := listener.Accept() if err != nil { continue } killer.recordPacket() conn.Close() } } func monitorUDP(killer *rateKiller) { conn, err := net.ListenPacket("udp", ":53") if err != nil { log.Printf("Warning: UDP monitor on :53 failed: %v", err) return } defer conn.Close() log.Println("🔍 UDP attack monitor active on :53") buf := make([]byte, 1024) for { _, _, err := conn.ReadFrom(buf) if err != nil { continue } killer.recordPacket() } } func homeHandler(w http.ResponseWriter, r *http.Request) { templates.ExecuteTemplate(w, "home.html", nil) } func registerHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { templates.ExecuteTemplate(w, "register.html", nil) return } if r.Method == "POST" { username := r.FormValue("username") password := r.FormValue("password") email := r.FormValue("email") fullName := r.FormValue("fullName") //Validacija if username == "" || password == "" || email == "" || fullName == "" { templates.ExecuteTemplate(w, "register.html", map[string]string{ "Error": "Vsa polja so obvezna!", }) return } // Preverimo če uporabnik že obstaja usersMux.RLock() _, exists := users[username] usersMux.RUnlock() if exists { templates.ExecuteTemplate(w, "register.html", map[string]string{ "Error": "Uporabniško ime že obstaja!", }) return } // Dodamo novega uporabnika addUser(username, password, email, fullName) // Preusmeri na prijavo http.Redirect(w, r, "/login?registered=1", http.StatusSeeOther) return } } func loginHandler(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { data := map[string]string{} if r.URL.Query().Get("registered") == "1" { data["Success"] = "Registracija je bila uspešna! Sedaj se lahko prijavite." } templates.ExecuteTemplate(w, "login.html", data) return } if r.Method == "POST" { username := r.FormValue("username") password := r.FormValue("password") // Preverimo podatke usersMux.RLock() user, exists := users[username] usersMux.RUnlock() if !exists || user.Password != password { templates.ExecuteTemplate(w, "login.html", map[string]string{ "Error": "Napačno uporabniško ime ali geslo!", }) return } // Uspešna prijava - preusmeri na dashboard // POZOR: Brez session management za demo! http.Redirect(w, r, "/dashboard?user="+username, http.StatusSeeOther) return } } func dashboardHandler(w http.ResponseWriter, r *http.Request) { username := r.URL.Query().Get("user") if username == "" { http.Redirect(w, r, "/login", http.StatusSeeOther) return } usersMux.RLock() user, exists := users[username] usersMux.RUnlock() if !exists { http.Redirect(w, r, "/login", http.StatusSeeOther) return } // Simuliramo "bančne" podatke accounts := []map[string]interface{}{ { "Name": "Glavni račun", "Number": "SI56 1234 5678 9012 345", "Balance": "€2,456.78", }, { "Name": "Varčevalni račun", "Number": "SI56 1234 5678 9012 999", "Balance": "€15,230.45", }, } data := map[string]interface{}{ "User": user, "Accounts": accounts, } templates.ExecuteTemplate(w, "dashboard.html", data) } func logoutHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/", http.StatusSeeOther) }