Twenty login tries before the backoff starts, not five

A guest password is typed by hand off a message on a phone. Five tries is not
many when a hyphen lands in the wrong place, and being told to wait is worse
than the brute-force risk here: past twenty the doubling lockout still applies,
the global backstop is unchanged, and the password is ~78 bits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Mitchell-Johnson
2026-09-27 18:04:23 -07:00
co-authored by Claude Opus 5
parent ed64aca63b
commit a0987c940f
+7 -4
View File
@@ -318,11 +318,10 @@ func (s *Sessions) RevokeOthers(user, keep string) int {
// RevokeUser ends every session of user (the account is going away). // RevokeUser ends every session of user (the account is going away).
func (s *Sessions) RevokeUser(user string) { s.RevokeOthers(user, "") } func (s *Sessions) RevokeUser(user string) { s.RevokeOthers(user, "") }
// Limiter is a single global backoff: after 5 failures, each further failure
// doubles a lockout starting at 30s, capped at 10 minutes. Single-user box.
// Limiter locks out a client after repeated failed logins, per client address, with a // Limiter locks out a client after repeated failed logins, per client address, with a
// global backstop so a botnet can't burn CPU on argon2 indefinitely. A lockout for one // global backstop so a botnet can't burn CPU on argon2 indefinitely. A lockout for one
// address never blocks another. // address never blocks another. Past perClientLock failures, each further failure doubles a
// lockout starting at 30s, capped at 10 minutes.
type Limiter struct { type Limiter struct {
mu sync.Mutex mu sync.Mutex
clients map[string]*bucket clients map[string]*bucket
@@ -338,7 +337,11 @@ type bucket struct {
var ErrLocked = errors.New("too many failed logins") var ErrLocked = errors.New("too many failed logins")
const ( const (
perClientLock = 5 // failures before a client is locked // perClientLock is how many failures one address gets before it is made to wait. Set high
// on purpose: a guest login is a generated password read off a message and typed by hand
// on a phone, and being locked out for fumbling it is worse than the brute-force risk —
// which the doubling backoff past this point, and the global backstop, still cover.
perClientLock = 20
globalLock = 100 // failures (all clients) in the window before everyone waits a minute globalLock = 100 // failures (all clients) in the window before everyone waits a minute
globalWindow = 5 * time.Minute globalWindow = 5 * time.Minute
) )