Replace Web Basic Auth With Cookie Sessions

Add bounded login challenges, CSRF/origin enforcement, logout, and
session-bound WebSocket admission. Isolate private HTTPD access behind a
version-guarded adapter and add focused host coverage. Also let empty
admin
SSH input reach the normal console handler.
This commit is contained in:
2026-09-05 23:55:05 +02:00
parent 4435a7fddd
commit 5a609fa40b
36 changed files with 1940 additions and 360 deletions
+22 -5
View File
@@ -4,6 +4,15 @@ This file is working memory. Update it during active work and before handoff; do
## Development state
- **8D.3 both-role target login confirmed / mixed-client evidence (2026-09-05):** User reports successful HTTPS user+admin login after Origin fix; previous login blocker is resolved. Settled internal/DMA/PSRAM free **71,204 / 63,448 / 8,247,744 B**. Mixed load free **33,868 / 26,112 / 8,089,060 B**, minima **13,756 / 6,000 / 8,072,744 B**, largest **25,600 / 25,600 / 7,995,392 B**; SSH stack minimum-free **16,288 B**. 115200 baud, four broker clients (SSH sole writer, USB + two web observers), user/admin SSH active. No reported web transport or SSH I/O errors; 6 login attempts/3 invalid credentials/1 logout, zero security rejections. Two identical loaded heap samples are not a soak/leak or reserve proof. Full details/provenance in `docs/phase8d3_implementation.md`. **M1 validation in progress, not signed off.**
- **Admin SSH empty Enter fix (2026-09-05):** User's empty line was classified as UART0-restricted because `remote_command_allowed` required argc>0. Changed only helper classification to allow empty input through normal quiet IDF handling; currentness and physical-only commands remain protected. New `python3 tests/admin_ssh_policy/run.py` passes 15 cases using production helper/installed parser. Build passes **21.04 s**, **95,508 B RAM / 1,625,725 B flash** (+20 B flash). Not uploaded or target-tested. Ask for empty Enter/normal-command smoke on next flash; no 8D.4 refactor.
- **8D.3 Origin-null fix (2026-09-05), target retest pending:** User confirmed challenge200/login403 with `Origin: null`, same-origin Fetch Metadata and pre-login cookie; post-attempt counters show 0 password attempts and 7 security rejections. Root cause is non-CORS fetch POST under no-referrer. Login fetch now uses `mode:'cors'`; app helper uses cors for POST tickets/logout, unchanged GET mode. Same-origin credentials/fixed paths/redirect denial/CSP/no-referrer and strict server Origin/CSRF remain intact. Login CSP hash updated atomically. Both UI suites and cookie-policy suite pass; build **95,508 B RAM / 1,625,705 B flash**, **14.30 s** (+16 B flash). No upload; actual Firefox Origin header/login/serial/logout retest and M1 acceptance still pending. See implementation record; supersedes the speculative diagnosis below.
- **8D.3 target login blocker (2026-09-05):** User supplied clean-boot/60-second settled telemetry, then reports both user/admin login rejected with the page's HTTP-403 message. Record in `docs/phase8d3_implementation.md`: internal free/min/largest 69,004/66,752/31,744 B; DMA 61,248/58,996/31,744 B; PSRAM 8,223,116/8,218,204/8,126,464 B; SSH stack minimum-free 18,464 B; no active sessions/broker clients, UART stopped. Boot counters precede login attempts; no post-attempt result yet. Exact flashed revision not supplied. Production login-renderer/CSP test passes. Browser warnings name other script hashes (possibly injected scripts), plus denied favicon/file URL; do not relax CSP based on these alone. Need failed endpoint/status/error code and nonsecret Origin/Sec-Fetch-Site. Investigation hypothesis: same-origin fetch mode plus no-referrer policy may serialize POST Origin as null; confirm wire headers before changing request policy. **M1 blocked, not signed off.** No corrective firmware change for this report yet.
- **8D.3 live cutover implemented / host-tested / build-verified (2026-09-05), M1 target pending:** Resumed another agent's uncommitted completed server/browser implementation; preserved it, verified installed IDF header/upgrade semantics, and fixed right-aligned pending-buffer cleanup with an actual-IDF-reader regression. Cookie login/logout is live, Basic/cache removed. Final build **95,508 B RAM / 1,625,689 B flash** (+248 / +23,764 B versus 8D.2). See `docs/phase8d3_implementation.md`. This supersedes older inert/planned statements below. **Stop for M1 target/browser sign-off before 8D.4; numeric reserve gates remain open.**
- **8D.3 inert login renderer completed (2026-09-05):** User requested continuation after the parser split. Added standalone `web_login_ui.{c,h}` plus production-renderer/Node tests; no live route or Basic-auth change. Build and focused suites pass; 8D.3/M1 remains incomplete. Remaining work is the atomic server/app cutover, then mandatory browser/target validation. See active task below.
- **8D.3 preparatory parser split (2026-09-05):** User requested continuation. Per the plan's 600800-line scope review, selected inert private request parsing before the larger atomic login/logout cutover. `src/web_auth_parse.{c,h}` and focused host tests added; no live HTTP callers or authentication changes. See active task below. 8D.3/M1 is **not complete**; Basic remains active. Prior 8D.2 user sign-off stands; numeric reserves remain open.
@@ -25,7 +34,7 @@ Based on checked-in source plus `README.md` and `docs/roadmap.md`:
- Hardware characterization, serial service, session broker, USB CDC, Wi-Fi, HTTPS/WebSocket, SSH serial transport, and local display/control are implemented and documented as target-hardware validated.
- Phase 8A role-based user storage/UART0 administration and Phase 8B role-aware HTTPS/SSH authentication and targeted revocation are documented as target-hardware validated.
- Phase 8C admin SSH is implemented in source, uses the shared `esp_console` registry, and has passed target-hardware validation.
- Phase 8D.1 is validated by user sign-off; 8D.2 serial/session binding is implemented, host-tested and build-verified with target regression pending. Browser login/logout and integrated web administration remain planned. Follow `docs/phase8d_plan.md`: one numbered chunk per request, target-validated login/logout (M1) before the browser admin shell (M2), then one typed-settings/control domain at a time (M3). Changing terminal modes must preserve the browser serial broker client and any writer lease. The roadmap retains the full end-state requirements.
- Phase 8D.08D.2 are validated by user sign-off; 8D.3 browser login/logout is implemented, host-tested and build-verified, with mandatory M1 target/browser validation pending. Browser administration remains planned. Follow `docs/phase8d_plan.md`: one numbered chunk per request, target-validated login/logout (M1) before the browser admin shell (M2), then one typed-settings/control domain at a time (M3). Changing terminal modes must preserve the browser serial broker client and any writer lease. The roadmap retains the full end-state requirements.
- Security/production hardening, OTA, BLE evaluation, advanced networking, and optional filesystem features remain future roadmap work.
- Reserved OTA, coredump, NVS-key, and storage partitions do not imply those runtime features are implemented.
@@ -44,8 +53,8 @@ Based on checked-in source plus `README.md` and `docs/roadmap.md`:
- Phase 8C hardware validation passed, including route separation, shared command serialization, history/completion, prompts, output backpressure, revocation during queued work, deferred SSH lifecycle/reboot actions, and full concurrent transport operation. At 460800 baud with SSH and WebSocket clients in parallel, substantial packet drops and slow display controls were observed under load, without memory exhaustion; no baud-rate reduction is planned.
- Current HTTPS has no web-based user administration and gives both roles the same status/terminal routes.
- Browser authentication still uses HTTP Basic; Phase 8D plans integrated login/logout sessions before exposing administrative browser routes.
- NVS encryption, secure boot/flash encryption review, authentication rate limiting, production certificate/provisioning policy, and OTA are not implemented.
- Browser authentication now uses cookie login/logout without Basic fallback. M1 target/browser validation gates any browser administrative routes.
- NVS encryption, secure boot/flash encryption review, production certificate/provisioning policy, and OTA are not implemented. HTTPS login has a bounded global five-verifications/60-second throttle, not comprehensive cross-transport DoS protection.
## Known inconsistencies
@@ -58,9 +67,17 @@ These observations should be checked when touching the relevant area; they are n
## Items to verify in future work
- Confirm task-local Newlib standard-stream behavior if ESP-IDF/Newlib configuration changes; admin SSH command output relies on dispatcher-task stream redirection.
- If HTTPD concurrency configuration changes, add locking around the boot-local Basic-authentication cache.
- Re-audit the private HTTPD adapter on SDK changes (including same-version patches): parsed-header layout, right-aligned pending data, explicit handshake/frame installation and log suppression. Host tests do not establish real socket behavior.
## Active Task - Phase 8D.3 Inert Login Rendering
## Active Task - Phase 8D.3 Live Authentication Cutover
- **Inherited implementation:** `web_cookie_auth.{c,h}`, `web_httpd_adapter.{c,h}`, parser optional-cookie validation, server/console/transport integration, browser app/session recovery, and focused suites. No Basic authorization/cache remains. Four one-hour sessions, four 120-second pre-login challenges, five password checks per 60-second window, four non-evicting 30-second tickets/two serial sockets. Fourteen URI slots, unchanged six HTTPS sockets and application task/stack/queue capacities. No generated assets or SDK files changed.
- **HTTPD decision:** Other agent chose an isolated **private-IDF adapter**, not the previously proposed SDK patch. Verified first-only header getters, append-only pointer-backed Set-Cookie (six-header successful login), auto-101-before-handler flow and private frame installation against installed 5.5.0. Serial URI uses ordinary GET until authenticated ticket/currentness/broker admission, then explicit handshake. Exact version guard requires re-audit on update; not a source-hash guarantee. HTTPD logs above ERROR compiled out to avoid secrets/ticket queries. Durable boundary recorded in architecture/design decisions and implementation record.
- **Fix in this continuation:** Pending HTTPD bytes are right-aligned. Inherited wipe preserved the wrong end, risking pipelined HTTP/early-frame corruption. Fixed consumed-prefix wipe and tested 0128 pending lengths plus partial reads using extracted installed `httpd_recv_pending`. Kept all other inherited source work intact.
- **Validation:** `python3 tests/web_cookie_auth/run.py`, parser 268-case suite, login UI eight Node groups, serial app nine Node groups, and store `--serial` integration mode pass. Cookie suite also executes store tests and extracts installed header getter/setter/pending-reader functions; handshake/network/tasks remain doubled. Final `pio run` passed **17.62 s**, **95,508 B RAM / 1,625,689 B flash**; +248/+23,764 B versus 8D.2, +976/+25,716 B versus recorded 8D.0. Auth symbols 637 B before placement padding (including 576 B challenges); Basic cache/key removal offsets much of it. No runtime reserve or stack margin inferred.
- **Handoff:** `docs/phase8d3_implementation.md` contains route policy, source verification, exact test commands/limits, accounting and M1 checklist. No upload/erase/commit/branch operation or real target/browser execution. Obtain M1 sign-off before 8D.4. Prior 8D.08D.2 sign-offs stand. Numeric heap/largest-block/owner-stack reserve floors, real cookie/CSP/bfcache, pre-101 rejection, frame/pipelined data, five-cycle lifecycle checks, isolation/revocation/expiry latency and full-client-mix soak/cleanup evidence remain pending.
## Previous Task - Phase 8D.3 Inert Login Rendering
- **Scope:** Second permitted preparatory split after scope review of remaining challenge/throttle/HTTP-route/application work. Added only `src/web_login_ui.{c,h}`, CMake registration, `tests/web_login_ui/` and documentation. Standalone renderer has no live HTTP caller or URI registration; Basic cache/auth, existing app and serial protocol, capacities, task stacks and generated assets are unchanged. No upload, erase, commit or branch change.
- **Behavior when integrated:** No fetch on page load; explicit Sign in obtains challenge with `X-Login-Bootstrap: 1`, then POSTs JSON with CSRF. Same-origin credentials/mode, no-store fetch, redirect rejection, 512-byte response bound and UTF-8 field/body limits. Generic safe-text errors, bounded Retry-After display/manual backoff, no automatic credential retry, fixed success navigation to `/`. Inputs disabled while pending; password fields/references cleared best-effort, attempt aborted on every exit, 15-second deadline, pagehide/pageshow generation guards. No localStorage/cookie access or logging; JavaScript/browser memory cannot be securely wiped.