Fix F-04 + F-11; add AUDIT.md

- AUDIT.md: security audit report with 16 findings (F-01..F-16)
- F-04 (server.go): wire loginRateLimit (10 req/s, burst 10) to
  POST /v1/auth/login and POST /v1/token/validate; no limit on
  /v1/health or public-key endpoints
- F-04 (server_test.go): TestLoginRateLimited uses concurrent
  goroutines (sync.WaitGroup) to fire burst+1 requests before
  Argon2id completes, sidestepping token-bucket refill timing;
  TestTokenValidateRateLimited; TestHealthNotRateLimited
- F-11 (ui.go): refactor Register() so all UI routes are mounted
  on a child mux wrapped with securityHeaders middleware; five
  headers set on every response: Content-Security-Policy,
  X-Content-Type-Options, X-Frame-Options, HSTS, Referrer-Policy
- F-11 (ui_test.go): 7 new tests covering login page, dashboard
  redirect, root redirect, static assets, CSP directives,
  HSTS min-age, and middleware unit behaviour
Security: rate limiter on login prevents brute-force credential
  stuffing; security headers mitigate clickjacking (X-Frame-Options
  DENY), MIME sniffing (nosniff), and protocol downgrade (HSTS)
This commit is contained in:
2026-03-11 20:18:09 -07:00
parent 4596ea08ab
commit 4da39475cc
6 changed files with 609 additions and 22 deletions

View File

@@ -53,11 +53,16 @@ func New(database *db.DB, cfg *config.Config, priv ed25519.PrivateKey, pub ed255
func (s *Server) Handler() http.Handler {
mux := http.NewServeMux()
// Security: per-IP rate limiting on public auth endpoints to prevent
// brute-force login attempts and token-validation abuse. Parameters match
// the gRPC rate limiter (10 req/s sustained, burst 10).
loginRateLimit := middleware.RateLimit(10, 10)
// Public endpoints (no authentication required).
mux.HandleFunc("GET /v1/health", s.handleHealth)
mux.HandleFunc("GET /v1/keys/public", s.handlePublicKey)
mux.HandleFunc("POST /v1/auth/login", s.handleLogin)
mux.HandleFunc("POST /v1/token/validate", s.handleTokenValidate)
mux.Handle("POST /v1/auth/login", loginRateLimit(http.HandlerFunc(s.handleLogin)))
mux.Handle("POST /v1/token/validate", loginRateLimit(http.HandlerFunc(s.handleTokenValidate)))
// Authenticated endpoints.
requireAuth := middleware.RequireAuth(s.pubKey, s.db, s.cfg.Tokens.Issuer)
@@ -93,7 +98,8 @@ func (s *Server) Handler() http.Handler {
}
uiSrv.Register(mux)
// Apply global middleware: logging and login-path rate limiting.
// Apply global middleware: request logging.
// Rate limiting is applied per-route above (login, token/validate).
var root http.Handler = mux
root = middleware.RequestLogger(s.logger)(root)
return root