fix(19): WR-06 stop sliding the rate-limit cooldown window on rejected attempts

This commit is contained in:
Lucas Berger
2026-06-17 20:31:07 -04:00
parent 30ad25c026
commit 4cf2ad4bff
+7 -2
View File
@@ -135,9 +135,14 @@ localAuthRouter.post('/local/login', zValidator('json', loginSchema, noEchoHook)
// Increment the counter even on 429 so continued brute-force accumulates toward lockout. // Increment the counter even on 429 so continued brute-force accumulates toward lockout.
if (live && live.count >= RATE_WINDOW_FAILURES && now < live.lockedUntil) { if (live && live.count >= RATE_WINDOW_FAILURES && now < live.lockedUntil) {
live.count += 1; live.count += 1;
live.lockedUntil = now + RATE_WINDOW_SECS * 1000; // WR-06: do NOT extend lockedUntil here. This request was itself REJECTED by the window;
// re-arming the cooldown on every blocked attempt let an attacker who keeps hammering the
// endpoint slide the window forward forever, so a legitimate user behind the same identity
// could never get back in even after pausing. The window stays anchored to when it was
// first armed (in the failure path below); it expires on schedule regardless of rejected
// traffic. The lockout (423) still triggers once the failure count crosses the threshold.
live.lockedOut = live.count >= LOCKOUT_FAILURES; live.lockedOut = live.count >= LOCKOUT_FAILURES;
if (live.lockedOut) live.lockedAt = now; if (live.lockedOut && live.lockedAt === 0) live.lockedAt = now;
loginAttempts.set(key, live); loginAttempts.set(key, live);
if (live.lockedOut) { if (live.lockedOut) {
return c.json({ error: 'Account locked' }, 423); return c.json({ error: 'Account locked' }, 423);