Harden auth: setup secret, throttling, session destroy, secure cookies.

Replace username-based admin bootstrap with a one-time setup secret, rate-limit login/register, equalize login bcrypt timing, cap passwords at 72 bytes, destroy sessions on logout, and require Secure cookies when PORT is set.
This commit is contained in:
2026-08-22 11:47:42 -07:00
parent 96b0ce795a
commit 59513ab75e
9 changed files with 287 additions and 50 deletions
+23 -10
View File
@@ -105,15 +105,18 @@ func loginUser(t *testing.T, h http.Handler, username, password string) []*http.
return post
}
func registerUser(t *testing.T, h http.Handler, username, password string) []*http.Cookie {
func registerUser(t *testing.T, h http.Handler, username, password string, setupSecret ...string) []*http.Cookie {
t.Helper()
rec := httptest.NewRecorder()
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/register", nil))
pre := rec.Result().Cookies()
preToken := sessionValue(pre)
csrf := csrfFrom(rec.Body.String())
form := strings.NewReader("_csrf=" + csrf + "&username=" + username + "&password=" + password)
req := httptest.NewRequest(http.MethodPost, "/register", form)
form := "_csrf=" + csrf + "&username=" + username + "&password=" + password
if len(setupSecret) > 0 && setupSecret[0] != "" {
form += "&setup_secret=" + setupSecret[0]
}
req := httptest.NewRequest(http.MethodPost, "/register", strings.NewReader(form))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
for _, c := range pre {
req.AddCookie(c)
@@ -179,25 +182,35 @@ func TestRegisterLoginAsk(t *testing.T) {
}
}
func TestAdminSeedOnlyWhenNoAdmins(t *testing.T) {
func TestAdminSetupSecretOnlyWhenNoAdmins(t *testing.T) {
mem := store.NewMemory()
secret := "one-time-admin-setup-secret"
adminName := uniq("seed")
srv := newTestServerStore(t, mem, Config{AdminUsername: adminName})
srv := newTestServerStore(t, mem, Config{AdminSetupSecret: secret})
h := srv.Handler()
registerUser(t, h, adminName, "hunter22")
plain := uniq("plain")
registerUser(t, h, plain, "hunter22")
uPlain, err := mem.UserByUsername(context.Background(), plain)
if err != nil || uPlain.Admin() {
t.Fatalf("register without setup secret must stay user: %+v %v", uPlain, err)
}
registerUser(t, h, adminName, "hunter22", secret)
u, err := mem.UserByUsername(context.Background(), adminName)
if err != nil || !u.Admin() {
t.Fatalf("first matching registrant should be admin: %+v %v", u, err)
t.Fatalf("setup secret registrant should be admin: %+v %v", u, err)
}
later := uniq("later")
srv2 := newTestServerStore(t, mem, Config{AdminUsername: later})
registerUser(t, srv2.Handler(), later, "hunter22")
srv2 := newTestServerStore(t, mem, Config{AdminSetupSecret: secret})
registerUser(t, srv2.Handler(), later, "hunter22", secret)
u2, err := mem.UserByUsername(context.Background(), later)
if err != nil {
t.Fatal(err)
}
if u2.Admin() {
t.Fatal("later admin username must stay user when an admin already exists")
t.Fatal("setup secret must not grant admin once an admin already exists")
}
}