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:
+23
-10
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user