From 01784219f83f68c7d03eb19c35f05eeaedc74487 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 7 Aug 2026 22:58:52 -0700 Subject: sockets: verify JWT signatures on the websocket auth path (S1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The websocket auth gate used jwt.decode(), which parses the payload but never checks the signature — anyone could forge {id: } (alg:none or any key), register their socket under the victim's uid, and receive every emit for that account. Switch both track() and the "login" gate to jwt.verify(token, config.jwt), rejecting forged/altered tokens. Also project the full-record socket emits through pick(user, whitelist) so a "user" event can never carry the bcrypt hash, TOTP secret, or PIN — matching the shape /login and /me already return. Co-Authored-By: Claude Opus 4.8 --- routes/users.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'routes') diff --git a/routes/users.ts b/routes/users.ts index 182c43aa..f10b728b 100644 --- a/routes/users.ts +++ b/routes/users.ts @@ -204,7 +204,7 @@ export default { user.twofa = false; await s(`user:${id}`, user); - emit(username, "user", user); + emit(username, "user", pick(user, whitelist)); emit(username, "otpsecret", user.otpsecret); l("disabled 2fa", username); res.send({}); @@ -221,7 +221,7 @@ export default { if (isValid) { user.twofa = true; await s(`user:${id}`, user); - emit(username, "user", user); + emit(username, "user", pick(user, whitelist)); } else { return res.code(500).send("Invalid token"); } @@ -365,8 +365,8 @@ export default { await s(`user:${user.id}`, user); if (user.nip5) await db.sAdd("nip5", `${user.username}:${user.pubkey}`); - emit(user.id, "user", user); - res.send({ user }); + emit(user.id, "user", pick(user, whitelist)); + res.send({ user: pick(user, whitelist) }); } catch (e) { warn("failed to update", user.username, e.message); bail(res, e.message); -- cgit v1.2.3