From 3eeaa6d9a33f9294ade64c8ff26144fba86a379e Mon Sep 17 00:00:00 2001 From: grm Date: Fri, 18 Sep 2026 13:47:49 +0300 Subject: Security: Add HTTPS and TRUST_PROXY settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The app only ever speaks plain HTTP, so it could not know the site was served over TLS: generated links were http:// (the /webadmin bounce from a blog host sent the login page over http), the session cookie was never Secure and nothing sent HSTS. HTTPS=true fixes all three; ClearSessionCookie now uses the same attributes as the set, since a browser only replaces a cookie whose Secure flag matches. TRUST_PROXY=true makes the client address the last X-Forwarded-For entry — the one our proxy appended — so throttling and the log see real addresses instead of the proxy's; earlier entries are whatever the client sent and are ignored. Production startup warns when HTTPS is off. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A --- internal/web/server.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'internal/web/server.go') diff --git a/internal/web/server.go b/internal/web/server.go index 12440de..f768ebd 100644 --- a/internal/web/server.go +++ b/internal/web/server.go @@ -43,6 +43,9 @@ func NewServer(cfg *config.Config, st *store.Store) *Server { // site plus the superadmin's root blog, one label below it is a blog, anything // else is a 404. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if s.cfg.HTTPS { // every subdomain is ours, so the whole site may pin https + w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains") + } host := hostname(r.Host) switch { case host == s.cfg.BaseDomain, host == config.RootSubdomain+"."+s.cfg.BaseDomain: @@ -178,13 +181,13 @@ func (s *Server) session(next http.Handler) http.Handler { } claims, err := auth.ParseToken(s.cfg.JWTSecret, c.Value) if err != nil { - auth.ClearSessionCookie(w) + auth.ClearSessionCookie(w, s.cfg.HTTPS) next.ServeHTTP(w, r) return } u, err := s.st.UserByID(r.Context(), claims.UserID) if err != nil || u.Disabled || u.TokenVersion != claims.TokenVersion { - auth.ClearSessionCookie(w) + auth.ClearSessionCookie(w, s.cfg.HTTPS) next.ServeHTTP(w, r) return } -- cgit v1.2.3