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:
@@ -98,19 +98,21 @@ TinyUSB callbacks enqueue/copy data and state; the transport task owns broker li
|
||||
|
||||
`web_server` runs HTTPS only on port 443 using the device-specific self-signed P-256 certificate from `web_security`. Current routes provide the UI, static assets, status, ticket issuance, and serial WebSocket upgrade.
|
||||
|
||||
HTTP Basic authentication uses `user_database`. Before administrator bootstrap, the migrated role-`user` account is synchronized from the legacy credential, so that username/password can authenticate through the database; after bootstrap, the legacy blob is independent recovery material and is no longer consulted for authentication or synchronized into role-based accounts. Both `user` and `admin` roles currently receive the same web status/terminal experience; web administration is not implemented.
|
||||
HTTPS login uses `user_database` and opaque server-side cookie sessions; Basic authentication and its cache are removed in 8D.3. Before administrator bootstrap, the migrated role-`user` account is synchronized from the legacy credential, so that username/password can authenticate through the database; after bootstrap, the legacy blob is independent recovery material and is no longer consulted for authentication or synchronized into role-based accounts. Both `user` and `admin` roles currently receive the same web status/terminal experience; web administration is not implemented.
|
||||
|
||||
The boot-local Basic-authentication cache has four RAM entries and a five-minute sliding lifetime. It stores a keyed digest of the complete `Authorization` header rather than the raw header, and every hit revalidates principal currentness. Its current lack of locking relies on the single-HTTPD-owner execution model.
|
||||
`web_cookie_auth` owns login/session/logout policy: four 120-second digest-only pre-login challenges, explicit same-origin bootstrap, five credential verifications per 60-second global window, and no live-record eviction. Host-only `__Host-` Secure/HttpOnly/SameSite=Strict cookies have absolute lifetimes. Login consumes a challenge, validates bounded JSON and issues a fresh session; logout invalidates only its originating session. Mutations require CSRF and strict canonical HTTPS Origin; serial upgrade requires matching cookie/Origin/ticket. Neither role has web administration yet.
|
||||
|
||||
Phase 8D.1 adds `web_session_store` primitives alongside Basic auth: four static records with token/origin digests, copied principal, separate CSRF state, one-hour absolute expiry and non-reused 64-bit session IDs. No HTTP handler issues cookie sessions yet. A portMUX protects short state copies/mutations; database/RNG/SHA calls occur outside it. Resolution rechecks ID/expiry after database validation; issuance also checks an invalidation epoch. Stop wipes records without resetting IDs/epochs. Only admitted HTTPS starts initialize the store; failed starts and accepted stops disable it before cleanup. Store-init failure cannot fail existing Basic HTTPS. Sensitive views must be wiped by callers; snapshots contain only counts and storage sizes. Focused host checks live in `tests/web_session_store/`.
|
||||
`web_session_store` holds four static records with token/origin digests, copied principal, separate CSRF state, one-hour absolute expiry and non-reused 64-bit session IDs. These are live cookie sessions in 8D.3, with no sliding renewal. A portMUX protects short state copies/mutations; database/RNG/SHA calls occur outside it. Resolution rechecks ID/expiry after database validation; issuance also checks an invalidation epoch. Stop wipes records without resetting IDs/epochs. Only admitted HTTPS starts initialize the store; failed starts and accepted stops disable it before cleanup. Authentication/store-init failure now gates HTTPS startup rather than falling back to Basic. Sensitive views must be wiped by callers; snapshots contain only counts and storage sizes. Focused host checks live in `tests/web_session_store/`.
|
||||
|
||||
Phase 8D.2 binds serial tickets/slots to distinct originating web-session IDs; zero is reserved for the shipped Basic path. Trusted internal mint/upgrade callers supply the ID; bound checks also compare the session's copied principal, with no CSRF export. Mint/consume/admission/input and existing 250 ms owner checks validate session liveness/currentness. Session-specific transport revocation invalidates the store first, then clears matching tickets and flags matching reserved/active slots for existing HTTPD/broker cleanup. Account/global transport revocation now invalidates cookie records even if serial initialization failed; existing console mutation callers reach these hooks unchanged. A non-wrapping transport epoch cancels in-flight ticket publication across revocation and server detach/re-attach. Store/database checks remain authoritative if notification is missed. Bound browser paths remain dormant until 8D.3's atomic cookie/CSRF/Origin cutover; no public cookie route, capacity change or new task exists in 8D.2.
|
||||
Serial tickets/slots bind to distinct originating web-session IDs; 8D.3 rejects zero instead of treating it as Basic. Trusted internal mint/upgrade callers supply the ID; bound checks also compare the session's copied principal, with no CSRF export. Mint/consume/admission/input and existing 250 ms owner checks validate session liveness/currentness. Session-specific transport revocation invalidates the store first, then clears matching tickets and flags matching reserved/active slots for existing HTTPD/broker cleanup. Account/global transport revocation now invalidates cookie records even if serial initialization failed; existing console mutation callers reach these hooks unchanged. A non-wrapping transport epoch cancels in-flight ticket publication across revocation and server detach/re-attach. Store/database checks remain authoritative if notification is missed. 8D.3 activates these checks for all browser routes, with five added authentication handlers (14 total), unchanged six HTTPS sockets and no new task.
|
||||
|
||||
A WebSocket connection requires a one-time, principal-bound ticket with a maximum 30-second lifetime. Only four tickets can be outstanding; minting another evicts the live entry with the earliest expiry. Ticket issuance and upgrade also validate a supplied `Origin` against `https://<Host>`; absence of `Origin` is accepted for non-browser clients. Tickets are stored as digests, consumed before currentness validation, and are never persisted. An admitted session starts the serial service if necessary, creates a broker client, and opportunistically requests writer ownership. The web transport has two fixed session slots. Binary frames carry serial data; small text messages request or release writer ownership. HTTPD owns socket send/close operations, while the web transport task mediates broker work through bounded scheduling. The browser's combined Connect/Disconnect control closes the WebSocket and pauses automatic reconnect; after a user-paused disconnect it changes to Connect, which resumes connection attempts.
|
||||
A WebSocket connection requires a one-time, principal-bound ticket with a maximum 30-second lifetime. Only four tickets can be outstanding; expired/stale identities are reclaimed and live capacity is rejected with 503/Retry-After, not eviction. Ticket issuance and upgrade require `Origin` matching validated Host after host-case/default-port normalization; missing Origin fails even for non-browser clients. Tickets are stored as digests, consumed before currentness validation, and are never persisted. An admitted session starts the serial service if necessary, creates a broker client, and opportunistically requests writer ownership. The web transport has two fixed session slots. Binary frames carry serial data; small text messages request or release writer ownership. HTTPD owns socket send/close operations, while the web transport task mediates broker work through bounded scheduling. The browser's combined Connect/Disconnect control closes the WebSocket and pauses automatic reconnect; after a user-paused disconnect it changes to Connect, which resumes connection attempts.
|
||||
|
||||
`web_httpd_adapter` is the sole private ESP-IDF 5.5.0 boundary. Its compile-time version guard requires review on upgrades. It validates NUL-separated parsed headers because public getters return only the first field, and rejects duplicates/ambiguous framing. The serial URI is registered as ordinary HTTP GET so cookie/ticket/principal/broker admission precedes explicit 101 and frame-handler installation; automatic IDF WebSocket routing would send 101 too early. Cleanup wipes consumed scratch but preserves right-aligned unread pending data. CMake compiles HTTPD logs above ERROR out to prevent header/ticket logging. No SDK patch or component copy exists. See `docs/phase8d3_implementation.md` for source verification, tests and pending on-wire checks.
|
||||
|
||||
Web serial initialization is failure-isolated from the base HTTPS service: if the transport cannot initialize, `web_server_init()` can still succeed and serve authenticated non-WebSocket routes.
|
||||
|
||||
`web_ui.c` contains authored index/application strings and response policy. Its restrictive CSP contains a hard-coded hash of the inline loader, so those two must change atomically; preserve same-origin connections, no-referrer behavior, frame denial, and the existing cache policy. `web_assets_data.c` contains checked-in generated arrays for vendored compressed xterm assets and the logo. Normal builds compile these arrays directly; they do not regenerate assets.
|
||||
`web_ui.c` contains authored index/application strings and response policy; it validates `/api/session` before connect/restore, adds explicit Sign out, and cancels stale work on 401/logout/page exit. `web_login_ui` is a standalone public page without protected-asset dependencies. Both authentication documents and app script are no-store. Its restrictive CSP contains a hard-coded hash of the inline loader, so those two must change atomically; preserve same-origin connections, no-referrer behavior, frame denial, and the existing cache policy. `web_assets_data.c` contains checked-in generated arrays for vendored compressed xterm assets and the logo. Normal builds compile these arrays directly; they do not regenerate assets.
|
||||
|
||||
### SSH
|
||||
|
||||
|
||||
@@ -61,17 +61,17 @@ This is a semantic map, not a complete file inventory. Start here, then read the
|
||||
**Responsibility:** serve authenticated HTTPS UI/API, issue WebSocket tickets, and adapt browser serial sessions to broker clients.
|
||||
|
||||
- Files: `src/web_server.{h,c}`, `src/web_serial_transport.{h,c}`, `src/web_ui.{h,c}`, `src/web_console.{h,c}`
|
||||
- Security files: `src/web_security.{h,c}`; `src/web_session_store.{h,c}` contains dormant Phase 8D.1 cookie-session primitives, not active HTTP authentication.
|
||||
- Security files: `src/web_security.{h,c}`, `src/web_cookie_auth.{h,c}`, `src/web_session_store.{h,c}`, `src/web_auth_parse.{h,c}`. Private IDF boundary: `src/web_httpd_adapter.{h,c}`.
|
||||
- Asset files: authored/generated boundary in `src/web_assets_data.{h,c}`, `web_assets/SOURCES.md`, `web_assets/generate_embedded_assets.py`
|
||||
- Interfaces: web init/start/stop/snapshots; HTTP handlers; ticket mint/consume; attach/detach; targeted session revocation
|
||||
- Called by: startup, ESP-IDF HTTPS server, user administration revocation, console/local UI
|
||||
- Dependencies: user database, secure random, broker, successful Wi-Fi manager initialization at boot, mbedTLS/HTTPS server; actual network reachability is an operational prerequisite, not an initializer invariant
|
||||
- Flow: `browser -> HTTPS Basic auth -> ticket -> WebSocket -> web transport -> broker`
|
||||
- Flow: `browser -> HTTPS login/cookie session -> CSRF-protected ticket -> cookie/Origin/ticket admission -> WebSocket -> web transport -> broker`
|
||||
- Ownership: HTTPD owns socket send/close work; transport task owns broker mediation; two fixed WebSocket slots and four outstanding tickets.
|
||||
- Security constraints: Basic-auth cache hits still revalidate principal currentness; the browser's combined Connect/Disconnect control closes the WebSocket and pauses automatic reconnect until Connect is selected. Changes to the authored inline loader must update its hard-coded CSP hash in the same change.
|
||||
- Session-store boundary: admitted HTTPS start initializes four static records; failed start/accepted stop disables and wipes them. 8D.2 binds tickets/slots to non-reused session IDs (zero only for Basic); transport-specific cleanup and account/global revocation invalidate store records before socket cleanup. No cookie route yet. RNG/SHA/database calls run outside short portMUX sections; ID/expiry/epoch checks reject stale work. Run `python3 tests/web_session_store/run.py` and its `--serial` integration mode.
|
||||
- 8D.3 preparation: `src/web_auth_parse.{c,h}` provides inert, allocation-free origin/cookie/login-JSON parsing; no HTTP caller yet. Test with `python3 tests/web_auth_parse/run.py`. These helpers do not authenticate or replace HTTP header/method/CSRF policy.
|
||||
- 8D.3 login rendering: `src/web_login_ui.{c,h}` contains a standalone no-store login document and hash-bound script; no registered route or live caller. `python3 tests/web_login_ui/run.py` checks production C rendering, CSP and Node DOM/fetch doubles. Existing `web_ui.c` and generated assets are unchanged.
|
||||
- Security constraints: Basic/cache removed; four absolute one-hour cookie sessions revalidate principal currentness. Four pre-login challenges (120 s), five credential attempts/60 s globally, no live session/challenge/ticket eviction. Origin/CSRF required for mutations; Origin/cookie/ticket before upgrade. Disconnect pauses reconnect but retains login; Sign out invalidates its session. Authored loader changes must update their hard-coded CSP hashes atomically.
|
||||
- Session-store boundary: admitted HTTPS start initializes records; auth-init failure gates HTTPS. Failed start/accepted stop disables and wipes state. Tickets/slots require nonzero non-reused session IDs; session/account/global revocation invalidates store records before socket cleanup. RNG/SHA/database calls run outside short portMUX sections; ID/expiry/epoch checks reject stale work. Run `python3 tests/web_session_store/run.py` and its `--serial` integration mode.
|
||||
- 8D.3 HTTP policy: `web_cookie_auth` owns public login/challenge/login POST/session/logout routes and protected-route checks; `web_auth_parse` handles bounded values/JSON. `web_httpd_adapter` alone reads private IDF 5.5.0 header scratch, rejects duplicate fields, defers 101 until transport admission and wipes consumed scratch while preserving right-aligned pending bytes. No SDK patch. `src/CMakeLists.txt` supplies private includes and compiles HTTPD warning/debug logs out. Test with `python3 tests/web_cookie_auth/run.py` and `python3 tests/web_auth_parse/run.py`.
|
||||
- 8D.3 UI: `src/web_login_ui.{c,h}` serves standalone `/login`; `web_ui.c` validates session before serial connect/restore and handles logout/401 safely. Both scripts hash-bound, auth documents/app no-store. Tests: `python3 tests/web_login_ui/run.py` and `python3 tests/web_ui_session/run.py`. Live cutover host-tested/build-verified, M1 target gate pending: `docs/phase8d3_implementation.md`.
|
||||
- Asset constraint: `web_assets_data.c` is checked-in generated input to the build; do not hand-edit or regenerate casually.
|
||||
|
||||
## SSH
|
||||
|
||||
@@ -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 600–800-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.0–8D.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 0–128 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.0–8D.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.
|
||||
|
||||
@@ -40,7 +40,7 @@ Only constraints supported by implementation or current project documentation be
|
||||
|
||||
**Consequence for future changes:** Preserve transport-slot generations and account-authentication generations as distinct concepts. Validate tokens immediately before side effects and discard late work after disconnect/reuse/revocation.
|
||||
|
||||
Phase 8D.2 adds a third identity: non-reused 64-bit originating web-session IDs in serial tickets/slots. Zero identifies only the existing Basic path until cutover. Session-specific cleanup must not become account-wide cleanup; account-name notification intentionally covers deletion/recreation. Invalidate cookie records before requesting transport cleanup, and retain authoritative session/principal checks when notification fails. The transport epoch cancels in-flight ticket publication without taking store and transport locks together.
|
||||
Phase 8D.2 adds a third identity: non-reused 64-bit originating web-session IDs in serial tickets/slots. 8D.3 rejects zero IDs; Basic authentication/cache are removed. Session-specific cleanup must not become account-wide cleanup; account-name notification intentionally covers deletion/recreation. Invalidate cookie records before requesting transport cleanup, and retain authoritative session/principal checks when notification fails. The transport epoch cancels in-flight ticket publication without taking store and transport locks together.
|
||||
|
||||
**Relevant files:** `src/session_broker.{h,c}`, `src/ssh_transport.c`, `src/web_serial_transport.c`, `src/admin_ssh_console.c`, `src/user_database.{h,c}`
|
||||
|
||||
@@ -94,6 +94,18 @@ Phase 8D.2 adds a third identity: non-reused 64-bit originating web-session IDs
|
||||
|
||||
**Relevant files:** `src/user_database.{h,c}`, `src/user_console.c`, `src/web_server.c`, `src/web_serial_transport.c`, `src/ssh_transport.c`
|
||||
|
||||
## Browser authentication has a narrow version-pinned HTTPD boundary
|
||||
|
||||
**Decision:** 8D.3 uses `web_cookie_auth` plus digest-only session/challenge stores, mandatory Origin/CSRF mutations and no live session/challenge/ticket eviction. Four one-hour absolute sessions deliberately interrupt long serial connections at expiry. No Basic compatibility path remains.
|
||||
|
||||
**Browser Origin serialization:** Authentication POST fetches use `mode: 'cors'` while retaining fixed same-origin URLs, `credentials: 'same-origin'`, redirect rejection and CSP `connect-src 'self'`. Under `no-referrer`, non-CORS POST mode can serialize Origin as `null` (confirmed in Firefox during M1 testing). Do not fix that by accepting null server-side or weakening CSP/referrer policy; no cross-origin server permission is added.
|
||||
|
||||
**HTTPD boundary:** `web_httpd_adapter` alone includes private ESP-IDF 5.5.0 structures. Public request getters expose only the first field, so the adapter validates bounded parsed headers/rejects duplicates. `/ws/serial` is an ordinary GET until authenticated transport admission explicitly sends 101 and installs the frame handler; automatic HTTPD upgrades happen before URI handlers. Preserve right-aligned unread pending bytes when wiping request memory. Two Set-Cookie calls append pointer-backed fields, whose distinct buffers must survive through send. HTTPD logs above ERROR are compiled out to prevent header/ticket exposure.
|
||||
|
||||
**Consequence:** The version guard is not a source-hash guarantee. Re-audit layout, scratch/pending ownership, logging and handshake/frame dispatch on SDK changes; do not scatter private accesses through application code or assume host doubles prove real socket behavior. No SDK patch is currently applied. See `docs/phase8d3_implementation.md` for verification and target gates.
|
||||
|
||||
**Relevant files:** `src/web_cookie_auth.{c,h}`, `src/web_session_store.{c,h}`, `src/web_httpd_adapter.{c,h}`, `src/web_server.c`, `src/web_serial_transport.c`, `src/CMakeLists.txt`.
|
||||
|
||||
## Security material and configuration use bounded, versioned NVS records
|
||||
|
||||
**Decision:** Application settings, users, and identities use separate fixed/versioned NVS blobs. Serial, Wi-Fi, mDNS-hostname, and local-UI working edits are RAM-only until explicitly saved. User mutations and HTTPS/SSH identity changes commit directly as part of the operation. Invalid ordinary configuration generally selects RAM defaults without erasing storage; malformed security material fails closed and needs explicit reset.
|
||||
|
||||
Reference in New Issue
Block a user