Files
2026-05-29 09:03:47 -04:00

2.6 KiB
Raw Permalink Blame History

Phase: 8 Sequence: 009 Slug: websocket-no-origin-validation Verdict: VALID Rationale: WebSocket server on port 3001 does not validate Origin header during upgrade handshake; combined with JWT-in-query-param, any website can initiate WebSocket connections using stolen tokens Severity-Original: medium Severity: medium PoC-Status: pending Pre-FP-Flag: none Debate: piolium/attack-surface/balanced-chamber-summary.md

Summary

The WebSocket server in web/src/server/websocket.ts does not validate the Origin header during the HTTP upgrade request. Combined with JWT authentication via query parameter, this means any website can initiate a WebSocket connection to the server on behalf of an authenticated user (if the user's JWT is known or leaked via p8-008).

Location

  • web/src/server/websocket.ts lines 80102 (connection handler)
  • web/src/server/websocket.ts lines 5667 (authentication)

Attacker Control

An attacker controlling a malicious website (e.g., evil.com) can initiate WebSocket connections to the server. If the attacker has obtained the victim's JWT (via log exposure, XSS, or other means), they can authenticate as the victim without Origin validation.

Trust Boundary Crossed

Cross-origin boundary. The WebSocket server accepts connections from any origin without validation, allowing cross-origin WebSocket connections that bypass same-origin policy protections.

Impact

Cross-origin WebSocket connections without Origin validation. Combined with JWT-in-query-parameter (p8-008), this creates a complete authentication bypass chain accessible from any website.

Evidence

wss.on("connection", async (ws: WsClient, req: IncomingMessage) => {
  const userId = await authenticateConnection(ws, req);
  // No Origin header check anywhere
  // req.origin is available but never inspected
  if (!userId) {
    ws.close(4001, "Authentication failed");
    return;
  }
  ws.userId = userId;
  addSocket(userId, ws);
});

Reproduction Steps

  1. Attacker controls a malicious website (evil.com)
  2. User is authenticated on Kordant (has valid JWT)
  3. If JWT is leaked (see p8-008), attacker crafts WebSocket connection from evil.com
  4. WebSocket server accepts the connection without Origin validation
  5. Attacker receives real-time alerts for the victim's account

Defense Search Results

  • No verifyClient option used on WebSocketServer
  • CORS middleware in web/src/middleware.ts does not apply to WebSocket upgrade (different handler, port 3001)
  • JWT verification validates signature and expiry but not origin
  • No per-user connection limit
  • Heartbeat timeout prevents unresponsive connections