diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-13 10:43:40 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-13 10:43:40 +0200 |
| commit | ab4657789eaca1d88b54e5d5123a0bc71a95e6ce (patch) | |
| tree | 4588be861beecf7e54d480b8fd30f54afd791fe4 /packages/meshbay-common/src | |
| parent | 3ce051e134a432417fcaca4e8b5775d98f614a31 (diff) | |
| download | meshbay-ab4657789eaca1d88b54e5d5123a0bc71a95e6ce.tar.gz | |
fix(hub): authenticate node WebSocket registration
Phase 11.5 — finding C2 (see second-review.md).
/v1/nodes/ws took node_id and group_ids straight from the client's first
message with no ownership check:
node_id = msg.get("node_id") or decoded.get("sub", "unknown")
_connected_nodes[node_id] = ws
Any registered user could connect with an ordinary browser token, claim a
victim node's id and overwrite its entry. Every WebRTC offer for that node was
then relayed to the attacker, who answered with their own SDP — full node
impersonation. The DTLS channel binding does not help, because the attacker is
the endpoint rather than a relay: the browser sends its GEK proof to the
attacker, who ignores it and replies handshake_ack. The attacker received the
victim's encrypted keypair bundle, chat and uploads, and could serve a forged
index.
Registration now requires scope == "node", verifies Node.user_id against the
token subject, checks the account is active, and refuses to displace a live
registration instead of silently overwriting it.
group_ids are intersected with the operator's actual membership: a node may
narrow the set to what it hosts but cannot widen it, so it cannot advertise
itself as an online source for arbitrary groups.
Authorization uses a short-lived session rather than Depends(get_db): a node
WebSocket lives for hours and a request-scoped dependency would pin a
PostgreSQL connection for its whole lifetime.
BEHAVIOUR: a node hosting a group whose hub membership was never recorded for
the operator's account will stop appearing in GET /v1/groups/{id}/nodes.
Adds tests/test_node_ws_auth.py (7 tests). The node WebSocket had no test
coverage at all, which is why this went unnoticed.
Tests: 109 node, 139 hub+common.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-common/src')
0 files changed, 0 insertions, 0 deletions