fix(ws): hard-deny inbox:* / typing:* when authX is empty
The WS topic-auth check had a soft-fail fallback: if the authenticated identity had no registered X25519 public key (authX == ""), the topic-ownership check was skipped and the client could subscribe to any inbox:* or typing:* topic. Exploit: register an Ed25519 identity without an X25519 key, subscribe to the victim's inbox topic, receive their envelope notifications. Now both topics hard-require a registered X25519. Clients must call REGISTER_KEY (publishing X25519) before subscribing. The scope is narrow — only identities that haven't completed REGISTER_KEY yet could have exploited this — but a hard fail is still correct. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
29
node/ws.go
29
node/ws.go
@@ -521,13 +521,17 @@ func (h *WSHub) authorizeSubscribe(c *wsClient, topic string) error {
|
|||||||
if authed == "" {
|
if authed == "" {
|
||||||
return fmt.Errorf("inbox:* requires auth")
|
return fmt.Errorf("inbox:* requires auth")
|
||||||
}
|
}
|
||||||
// If we have an x25519 mapping, enforce it; otherwise accept
|
// Hard-require a registered X25519 identity — otherwise an
|
||||||
// (best-effort — identity may not be registered yet).
|
// Ed25519-only identity could subscribe to ANY inbox topic by
|
||||||
if authX != "" {
|
// design (authX == "" skipped the equality check). Fixed: we
|
||||||
want := strings.TrimPrefix(topic, "inbox:")
|
// now refuse the subscription until the client publishes an
|
||||||
if want != authX {
|
// X25519 key via REGISTER_KEY.
|
||||||
return fmt.Errorf("inbox:* only for your own x25519")
|
if authX == "" {
|
||||||
}
|
return fmt.Errorf("inbox:* requires a registered X25519 identity (send REGISTER_KEY first)")
|
||||||
|
}
|
||||||
|
want := strings.TrimPrefix(topic, "inbox:")
|
||||||
|
if want != authX {
|
||||||
|
return fmt.Errorf("inbox:* only for your own x25519")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -536,11 +540,12 @@ func (h *WSHub) authorizeSubscribe(c *wsClient, topic string) error {
|
|||||||
if authed == "" {
|
if authed == "" {
|
||||||
return fmt.Errorf("typing:* requires auth")
|
return fmt.Errorf("typing:* requires auth")
|
||||||
}
|
}
|
||||||
if authX != "" {
|
if authX == "" {
|
||||||
want := strings.TrimPrefix(topic, "typing:")
|
return fmt.Errorf("typing:* requires a registered X25519 identity")
|
||||||
if want != authX {
|
}
|
||||||
return fmt.Errorf("typing:* only for your own x25519")
|
want := strings.TrimPrefix(topic, "typing:")
|
||||||
}
|
if want != authX {
|
||||||
|
return fmt.Errorf("typing:* only for your own x25519")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user