fix: review findings on the login throttle

- The throttle runs on a 64-bit clock in ms (esp_timer_get_time()), so a
  slot left alone for 49.7 days isn't brought back by millis() wrapping
  round to its old failure.
- Web.cpp turns the client into the throttle's key in one place, and
  says that an IPv6 client would share key 0; names are clearer
  (retryMs, vacantSlot).
- The shared slot's test now succeeds from the IP counted in it, so a
  success that cleared the shared slot would fail it.
- GLOSSARY.md names it the login throttle, with lockouts, and
  SECURITY_PLAN.md and ADR 0006 use that name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cjkas 2026-10-27 18:44:39 +01:00
parent f8e97fb6dd
commit ff7517e10f
6 changed files with 53 additions and 30 deletions

View file

@ -54,7 +54,7 @@
- **Comparison:** constant-time, used for HTTP and the websocket handshake alike.
- **Migration:** none needed. The browser logs in again when its token is refused, and HA logs in again at runtime.
### Login throttling (PR C)
### Login throttle (PR C)
- Per client IP: after 5 failed `/login` attempts, refuse that IP for 30 s, doubling each time up to 15 min. A success resets it.
- After the first lockout, each failure starts the next one, so an IP gets one try per lockout rather than five.
- An hour without a failure forgets the IP.
@ -87,7 +87,7 @@ Protocol 2 lands as one PR per step, and `main` keeps working throughout because
| 3 | HA | protocol 2 with the old-firmware fallback; `/loginContext` setup; runtime login and reauth |
| 4 | firmware | removals: the old routes, ports 8081 and 8080, `/discovery`, ModelSnapshot, "Secure Config Only"; static serving limited to web assets; CORS headers; Origin check |
PR B (token key) and PR C (login backoff) are separate firmware PRs that can land at any point. The GitHub update was removed ahead of the rest ([ADR 0007](adr/0007-no-github-update.md)), as protocol 2 didn't fit beside it.
PR B (token key) and PR C (login throttle) are separate firmware PRs that can land at any point. The GitHub update was removed ahead of the rest ([ADR 0007](adr/0007-no-github-update.md)), as protocol 2 didn't fit beside it.
## Tests
@ -95,7 +95,7 @@ The rules go into small pure helpers with native tests, following [src/Secrets.h
- which routes are public
- the token comparison
- the Origin/Host match
- the backoff schedule
- the login throttle's schedule
The e2e suite (`test/e2e`) checks, when given credentials, that a socket without a token is refused, that the HTTP routes answer 401, and that a token works. It never changes the device's security settings itself.