hub v0.3.8 — CSRF protection + secure session model

- server.go: replace literal hub_session=authenticated with random 64-char hex
  session tokens stored server-side (hubSession map + sync.RWMutex); per-session
  CSRF tokens; CleanupSessions goroutine; SameSite=Lax+Secure cookie; CSRF
  validation in ServeHTTP; csrfToken/csrfField helpers
- configs.go: add html/template import; pass CSRFField/CSRFToken to all template
  renders; renderConfigForm gains r *http.Request parameter
- config_form.html: {{.CSRFField}} in form
- customer_unified.html: meta csrf-token + csrfHeaders() JS; {{.CSRFField}} in
  all 5 POST forms; csrfHeaders() on 3 fetch calls
- main.go: start CleanupSessions goroutine

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 16:39:14 +01:00
parent da991fad57
commit 67f53a4ccd
6 changed files with 187 additions and 21 deletions
+18
View File
@@ -158,6 +158,24 @@ The asset manager (`internal/assets/`) scans the assets directory on startup, bu
Protected by bcrypt password + session cookie (7-day expiry).
### Authentication & Session Model (`internal/web/server.go`)
- Login generates a **cryptographically random 64-char hex session token** stored server-side in a `map[string]*hubSession` (+ `sync.RWMutex`). The old literal `hub_session=authenticated` cookie is gone.
- Each session also stores a **per-session CSRF token** (separate 64-char hex random value).
- Cookie attributes: `SameSite=Lax`, `Secure` (when TLS), `HttpOnly`, 7-day `Max-Age`.
- `RequireAuth` middleware validates the session token with `subtle.ConstantTimeCompare` and redirects to `/login` on failure.
- `CleanupSessions(ctx)` goroutine runs hourly to purge expired sessions.
### CSRF Protection (`internal/web/server.go`)
Synchronizer-token CSRF protection on all browser POST/DELETE/PATCH operations:
- CSRF validation block runs at the top of `ServeHTTP` before routing.
- Skipped when: no session cookie present (API/Basic-Auth path); or safe methods (GET/HEAD/OPTIONS).
- Token read from `_csrf` form field or `X-CSRF-Token` request header.
- On failure: JSON `{"ok":false,"error":"CSRF token missing or invalid"}` for `/api/` paths; HTTP 403 text otherwise.
- Template delivery: `csrfToken(r)` and `csrfField(r)` helpers inject `CSRFToken` and `CSRFField` into every render data struct via `configs.go`. Templates use `{{.CSRFField}}` in forms and `csrfHeaders()` JS helper for fetch calls.
### Pages
- **Dashboard (`/`)** — Fleet overview table showing all customers with live status and event count badges (error+warning in last 24h). Config-only customers (no reports yet) appear as "PENDING" with gray badge. Blocked customers are hidden. Auto-refreshes every 60 seconds.