feat: add configurable maxPayloadSize for WebSocket#4955
feat: add configurable maxPayloadSize for WebSocket#4955
Conversation
- Add webSocketOptions.maxDecompressedMessageSize to DispatcherBase - Propagate through Agent, Client, Pool - Increase default from 4 MB to 64 MB - Add estimated expansion check (10x ratio) - Fix actual size tracking to use configured limit - Add tests
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4955 +/- ##
==========================================
+ Coverage 92.93% 93.03% +0.09%
==========================================
Files 112 111 -1
Lines 35725 35938 +213
==========================================
+ Hits 33202 33434 +232
+ Misses 2523 2504 -19 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
KhafraDev
left a comment
There was a problem hiding this comment.
My issues are still present. By default, code that works in ws, Bun, deno, firefox, chrome, Safari, and every other browser and environment will fail in node.
The security model of Firefox, Chrome and Safari is radically different from what we can offer in Node.js. The challenges are sometimes opposite. Out-of-memory errors cannot be easily caught by Node.js and V8 (Chrome can just crash a tab) and they are handled as best-effort cases. The only safe way to prevent a crash is to provide a solution to avoid allocating that memory. I will investigate Bun and Deno if they deal with this in some different way. |
|
Different security models, but the same spec. |
|
I did a pass over what limits other WebSocket implementations actually expose, because RFC 6455 leaves this up to implementations. A few things seem worth separating:
1) What the spec saysRFC 6455 explicitly says this is implementation-specific. In §10.4:
Ref: https://www.rfc-editor.org/rfc/rfc6455#section-10.4 So the spec does not define one universal limit, but it does explicitly expect implementations to protect themselves. 2) Browser-style WebSocket APIsFor Chrome / Firefox / Safari, I couldn't find an official public doc that says “the maximum incoming WebSocket message size is N bytes”. What MDN does document for the standard browser Deno's browser-style
So for browser-style APIs, the public contract appears to be closer to "implementation / memory dependent" than to a stable documented numeric cap. 3) Server/runtime implementations that do expose explicit limitsThese are the clearest documented limits I found:
4) What this means for undiciThe cross-implementation picture looks like this:
Also, most of the limits above are message-size limits in general, not specifically decompressed-message-size limits. In that sense, what this PR adds is actually more targeted than many existing APIs, because it directly addresses the So IMO this PR is well aligned with the ecosystem:
|
|
Note that permessage-deflate is standardized in RFC 7692 and not RFC 6455
|
- Set maxDecompressedMessageSize to 0 to disable the limit - Default remains 64 MB for decompression bomb protection - Add test for disabled limit
|
I doesn't matter if it's server or client. If one is receiving data from untrusted sources, they are vulnerable to this attack (in Node.js). I'll keep digging on more proof points. |
|
A bit more data after testing other runtimes in the same setup (host
References:
Given that, one question: would it make sense to also consider a more general My intuition is that |
|
I'm also ok in raising the limit to 64MB, I was too conservative with 4MB |
|
I'm inclined to think we should be cautious about introducing limits like 64MB that aren’t explicitly in the specification. Since the client can't currently restrict uncompressed messages, limiting only the compressed path might not provide full protection against OOM crashes; raw data could still potentially be streamed up to the 2GB limit. Unless we can implement a unified maxPayloadSize for both paths, it might be more consistent to default to the protocol or platform maximum (approx. 2GB). It may be better to let developers configure specific restrictions themselves, rather than having the library impose caps that aren't standard in browser environments. |
No, it is there since forever, the commit you are referring to is just moving a positional argument to an option.
No, it works in the same way for both the client and the server. 0 means no limit. The extension is enabled by default on the client and is disabled by default on the server due to memory fragmentation issues, see https://github.com/websockets/ws?tab=readme-ov-file#websocket-compression. Anyway a lot has changed/improved since the defaults decision and it is now used in many production environments. You can count the size of the chunks in the listener of the |
My bad, in jsdoc
This is what the PR is doing. |
Yes, but the class is not instantiated with that value (https://github.com/websockets/ws/blob/84392554/lib/websocket-server.js#L299, https://github.com/websockets/ws/blob/84392554/lib/websocket.js#L761). |
- Apply limit to both compressed and uncompressed payloads - Add raw payload size check before accepting uncompressed data - Update types and docs to reflect new option name - Add test for raw uncompressed payload limit enforcement
Fixes #4944
Changes
Usage