Add CSRF protection to state-changing POST endpoints

Add gorilla/csrf middleware to protect all HTML-serving routes against
cross-site request forgery attacks. The webhook endpoint is excluded
since it uses secret-based authentication.

Changes:
- Add gorilla/csrf v1.7.3 dependency
- Add CSRF() middleware method using session secret as key
- Apply CSRF middleware to all HTML route groups in routes.go
- Pass CSRF token to all templates via addGlobals helper
- Add {{ .CSRFField }} / {{ $.CSRFField }} hidden inputs to all forms

Closes #11
This commit is contained in:
clawbot
2026-02-15 14:17:55 -08:00
parent d4eae284b5
commit b1dc8fcc4e
17 changed files with 77 additions and 30 deletions

View File

@@ -37,18 +37,22 @@ func (s *Server) SetupRoutes() {
http.FileServer(http.FS(static.Static)),
))
// Public routes
s.router.Get("/login", s.handlers.HandleLoginGET())
s.router.Post("/login", s.handlers.HandleLoginPOST())
s.router.Get("/setup", s.handlers.HandleSetupGET())
s.router.Post("/setup", s.handlers.HandleSetupPOST())
// Webhook endpoint (uses secret for auth, not session)
// Webhook endpoint (uses secret for auth, not session — no CSRF)
s.router.Post("/webhook/{secret}", s.handlers.HandleWebhook())
// Protected routes (require session auth)
// All HTML-serving routes get CSRF protection
s.router.Group(func(r chi.Router) {
r.Use(s.mw.SessionAuth())
r.Use(s.mw.CSRF())
// Public routes
r.Get("/login", s.handlers.HandleLoginGET())
r.Post("/login", s.handlers.HandleLoginPOST())
r.Get("/setup", s.handlers.HandleSetupGET())
r.Post("/setup", s.handlers.HandleSetupPOST())
// Protected routes (require session auth)
r.Group(func(r chi.Router) {
r.Use(s.mw.SessionAuth())
// Dashboard
r.Get("/", s.handlers.HandleDashboard())
@@ -90,6 +94,7 @@ func (s *Server) SetupRoutes() {
// Ports
r.Post("/apps/{id}/ports", s.handlers.HandlePortAdd())
r.Post("/apps/{id}/ports/{portID}/delete", s.handlers.HandlePortDelete())
})
})
// Metrics endpoint (optional, with basic auth)