This repo includes an example tRPC 11 WebSocket server and proof of concept vulnerability. It's based on the express-minimal
example from here and then has the suggested WebSocket configuration applied from here.
Any tRPC 11 server with WebSocket enabled with a createContext
method set is vulnerable. Here is an example:
trpc-vuln.mp4
The connectionParams logic introduced in trpc/trpc#5839 does not safely handle invalid connectionParams objects. During validation if the object does not match an expected shape an error will be thrown:
This is called during WebSocket connection setup inside createCtxPromise()
here:
createCtxPromise
has handling to catch any errors and pass them up to the opts.onError
handler:
However the error handler then rethrows the error:
Since this is all triggered from the WebSocket message
event there is no higher level error handling so this causes an uncaught exception and crashes the server process.
This means any tRPC 11 server with WebSockets enabled can be crashed by an attacker sending an invalid connectionParams object. It doesn't matter if the server doesn't make user of connectionParams, the connectionParams logic can be initiated by the client.
To fix this vulnerability tRPC should not rethrow the error after it's be handled. This patch fixes the vulnerability:
From 5747b1d11946f60268eb86c59784bd6f7eb50abd Mon Sep 17 00:00:00 2001
From: Luke Childs <[email protected]>
Date: Sun, 20 Apr 2025 13:27:10 +0700
Subject: [PATCH] Don't throw already handled error
This error has already been handled so no need to re-throw. If we re-throw it will not be caught and will trigger an uncaught exception causing the entire server process to crash.
---
packages/server/src/adapters/ws.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/packages/server/src/adapters/ws.ts b/packages/server/src/adapters/ws.ts
index ad869affd..5a578b5cb 100644
--- a/packages/server/src/adapters/ws.ts
+++ b/packages/server/src/adapters/ws.ts
@@ -167,8 +167,6 @@ export function getWSConnectionHandler<TRouter extends AnyRouter>(
(globalThis.setImmediate ?? globalThis.setTimeout)(() => {
client.close();
});
-
- throw error;
});
}
--
2.48.1