Skip to content

Security: WebSocket proxy has no message size limit or idle timeout (DoS vector) #37

Description

@ThankNIXlater

Summary

The WebSocket proxy in server/gateway-proxy.js forwards all messages between browser and upstream gateway without size constraints or connection timeouts. This enables denial of service against both the Studio server and the upstream gateway.

Location

  • server/gateway-proxy.js (entire browserWs.on("message") handler)

Technical Detail

Three gaps:

1. No message size limit

The ws library's default maxPayload is 100MB (v8.x). Every incoming message is parsed with JSON.parse(String(raw)). A malicious browser client can send payloads up to 100MB that get fully buffered and parsed, causing memory pressure and CPU spikes.

browserWs.on("message", async (raw) => {
  const parsed = safeJsonParse(String(raw ?? "")); // No size check before parsing
  // ...
});

2. No connect timeout

If a browser opens a WebSocket but never sends a connect frame, the connection stays open indefinitely. The upstream WebSocket is also opened and held open.

3. No rate limiting

A browser client can send thousands of messages per second, all forwarded to the upstream gateway.

Impact

  • Memory exhaustion on the Studio server from large payloads
  • Connection exhaustion from idle/zombie connections
  • Upstream gateway flooding from rapid message forwarding
  • A single malicious browser tab can degrade service for all users

Suggested Fix

// In createGatewayProxy, set maxPayload on the WebSocketServer:
const wss = new WebSocketServer({
  noServer: true,
  verifyClient,
  maxPayload: 1_048_576, // 1MB - generous for JSON control frames
});

// Add connect timeout in the connection handler:
let connectTimeout = setTimeout(() => {
  closeBoth(1008, "connect timeout");
}, 10_000); // 10 seconds to send connect frame

// Clear timeout when connect frame arrives:
if (connectRequestId && connectTimeout) {
  clearTimeout(connectTimeout);
  connectTimeout = null;
}

The maxPayload option on the ws WebSocketServer will reject oversized frames at the protocol level before they reach the message handler.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions