package internals import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "net/http" "strings" "time" ) // ── CSRF ────────────────────────────────────────────────────────────── func newCSRFToken() string { b := make([]byte, 24) rand.Read(b) //nolint:errcheck return hex.EncodeToString(b) } // setCSRFCookie writes a fresh CSRF token to a short-lived cookie and returns // the token value so it can be embedded in the rendered HTML form. func setCSRFCookie(w http.ResponseWriter) string { tok := newCSRFToken() http.SetCookie(w, &http.Cookie{ Name: csrfCookieName, Value: tok, Path: "/", HttpOnly: true, Secure: true, SameSite: http.SameSiteStrictMode, MaxAge: 900, // 15 min — covers slow typists }) return tok } // checkCSRF returns true iff the submitted csrf_token form field matches the // cookie value (constant-time compare to prevent timing side-channels). func checkCSRF(r *http.Request) bool { c, err := r.Cookie(csrfCookieName) if err != nil || c.Value == "" { return false } formTok := r.FormValue("csrf_token") if formTok == "" { return false } return hmac.Equal([]byte(c.Value), []byte(formTok)) } func checkCreds(username, password string) bool { if username != appCreds.Username { return false } got := hashPassword(password, appCreds.Salt) return hmac.Equal([]byte(got), []byte(appCreds.Hash)) } func initAuthSecret() { authSecret = make([]byte, 32) rand.Read(authSecret) } func makeAuthToken() string { ts := fmt.Sprintf("%d", time.Now().Unix()) mac := hmac.New(sha256.New, authSecret) mac.Write([]byte(ts)) return ts + "." + hex.EncodeToString(mac.Sum(nil)) } func validAuthToken(token string) bool { dot := strings.LastIndex(token, ".") if dot < 0 { return false } ts, sig := token[:dot], token[dot+1:] mac := hmac.New(sha256.New, authSecret) mac.Write([]byte(ts)) if !hmac.Equal([]byte(sig), []byte(hex.EncodeToString(mac.Sum(nil)))) { return false } var t int64 fmt.Sscanf(ts, "%d", &t) return time.Since(time.Unix(t, 0)) < authTokenTTL } func isAuthed(r *http.Request) bool { if nopwMode { return true } c, err := r.Cookie(authCookieName) return err == nil && validAuthToken(c.Value) }