Commit Graph
1 Commits
Author SHA1 Message Date
popertots 1179293777 fix: 2FA single-use consume is DB-atomic; login lockout escalates 15/30/60m with uniform 401
F5.5 brute-force hardening:
- internal/twofa.Check consume is now a conditional UPDATE (WHERE id AND
  two_factor_pending_code_hash) reporting rows affected: two concurrent
  verifications of the same code on different instances both match the digest,
  but only the first conditional UPDATE can affect a row — the loser sees 0
  rows and fails MissingOrExpired, so one code authorizes exactly ONE operation
  across instances (the per-user mutex only serialized within one process).
- /login lockout is now indistinguishable from a wrong password: a locked
  account returns the same uniform 401 'invalid credentials' and burns the same
  constant-time bcrypt compare (via the shared semaphore), removing the
  account-existence oracle and lockout-probing signal of the old 429.
- Lockout tiers escalate 15m (5+) / 30m (7+) / 60m (10+): an attacker who keeps
  guessing past each unlock makes the lock LONGER, raising the repeat-DoS
  effort while the response stays uniform.
2026-08-22 00:34:50 +01:00