From 254733b0566830a46e5a44c2d3127f57bfaceb65 Mon Sep 17 00:00:00 2001 From: grm Date: Fri, 18 Sep 2026 13:46:33 +0300 Subject: Security: Cap the login body and log failed logins POST /webadmin is the one form outside guardPOST, so nothing bounded its body: a multipart login could park 32 MB in memory or temp files per request. It now reads at most 64 KB. Wrong passwords are logged with the username and client address so an attack shows up in the log (fail2ban can read it) instead of being invisible. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A --- internal/web/web_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'internal/web/web_test.go') diff --git a/internal/web/web_test.go b/internal/web/web_test.go index 6324443..49915f4 100644 --- a/internal/web/web_test.go +++ b/internal/web/web_test.go @@ -962,3 +962,16 @@ func TestLoginThrottled(t *testing.T) { t.Errorf("got %d, want 429", rec.Code) } } + +// The login form is the one POST outside guardPOST; it caps its own body. +func TestLoginBodyCapped(t *testing.T) { + s := NewServer(&config.Config{BaseDomain: "example.com", JWTSecret: []byte("x")}, nil) + rec := httptest.NewRecorder() + req := httptest.NewRequest("POST", "/webadmin", strings.NewReader("username=a&password="+strings.Repeat("x", maxLoginBody))) + req.Host = "example.com" + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + s.ServeHTTP(rec, req) + if rec.Code != http.StatusBadRequest { + t.Errorf("got %d, want 400", rec.Code) + } +} -- cgit v1.2.3