https://gitlab.synchro.net/main/sbbs/-/commit/3899ab7cae4222373328a5ca
Modified Files:
docs/superpowers/specs/2026-07-31-hashed-password-storage-design.md
Log Message:
docs: webv4 breaks under hashed passwords, in two ways
The cleartext-sink list did not reach the web UIs, which is where the
most visible breakage is.
The page-content half is the RLogin auto-login convention: the password
travels in the client-user-name field, compared against useron.pass at answer.cpp:170, so webv4/pages/003-games.xjs:64 writes
user.security.password straight into the served HTML as Options.RLoginClientUsername. It is rendered into JavaScript source, sent
to the browser, and sent back through the WebSocket-to-RLogin proxy when
a door is launched. web/root/members/externals.ssjs:60 does the same in
the v3 UI, web/lib/leftnav_html.ssjs:8 builds
ftp://alias:password@host
into the left nav of every page, and rlogin.js:86,90 is the door-side
form -- its -h variant is no escape, since sha1(password + ...) still has
to read the cleartext to compute it.
The larger half is session resumption. validateSession() (webv4/lib/auth.js:106) re-establishes a cookie-carried session by
reading the stored password back out and passing it to login(); guest
login (:229) does the same with the Guest account's password. No pass.tab
value makes either succeed, so under hashing every webv4 session fails on
the request after the form login and the guest path fails before anyone
logs in. Both go in the sinks table, in the key-or-token table, and in a
prose block splitting the two failures; the answer to both is a
short-lived single-use token bound to user number and client IP, accepted
in the client-user-name slot, with the session record's existing key made
the authenticated object.
That also corrects a claim in Constraints. webv4's form login runs the
KDF once, but the cookie path re-authenticates through login() on every request, so as written it costs a derivation per request exactly as HTTP
Basic does -- making the session the authenticated object is a cost fix
there as well as a correctness fix under hashing.
Sequencing step 4 gains the note that neither webv4 site has a
compare-only form, so both are their own work and must land before the
storage change rather than folding into the mechanical verify_password() conversion.
Co-Authored-By: Claude Opus 5 (1M context) <
noreply@anthropic.com>
---
þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net