Socket.IO version 2.1.1 represents a minor update to the popular real-time communication library, building upon the foundation established by version 2.1.0. Both versions provide a robust framework for creating real-time applications leveraging Node.js. Key dependencies like engine.io, has-binary2, socket.io-parser, and socket.io-adapter remain consistent, ensuring core functionality is maintained. Developers familiar with 2.1.0 will find a smooth transition to 2.1.1.
The most notable difference lies in the updated socket.io-client dependency within version 2.1.1; aligning the client and server versions for optimal compatibility. This subtle change likely addresses bug fixes or enhancements in the client-side component, warranting a point release. For developers, ensuring the client-side library matches the server version is crucial for seamless communication.
While the development dependencies, including testing frameworks like mocha, expect.js, supertest, and superagent, remain the same; the update suggests continued commitment to quality assurance. The updated releaseDate and slightly increased unpackedSize in version 2.1.1 reflect the changes incorporated in this minor release, indicating iterative improvements that contribute to the ongoing stability and reliability of Socket.IO. Therefore, upgrading to 2.1.1 is recommended for developers seeking the latest refinements and ensuring alignment between client and server components.
All the vulnerabilities related to the version 2.1.1 of the package
CORS misconfiguration in socket.io
The package socket.io before 2.4.0 are vulnerable to Insecure Defaults due to CORS Misconfiguration. All domains are whitelisted by default.
socket.io has an unhandled 'error' event
A specially crafted Socket.IO packet can trigger an uncaught exception on the Socket.IO server, thus killing the Node.js process.
node:events:502
throw err; // Unhandled 'error' event
^
Error [ERR_UNHANDLED_ERROR]: Unhandled error. (undefined)
at new NodeError (node:internal/errors:405:5)
at Socket.emit (node:events:500:17)
at /myapp/node_modules/socket.io/lib/socket.js:531:14
at process.processTicksAndRejections (node:internal/process/task_queues:77:11) {
code: 'ERR_UNHANDLED_ERROR',
context: undefined
}
| Version range | Needs minor update? |
|------------------|------------------------------------------------|
| 4.6.2...latest
| Nothing to do |
| 3.0.0...4.6.1
| Please upgrade to socket.io@4.6.2
(at least) |
| 2.3.0...2.5.0
| Please upgrade to socket.io@2.5.1
|
This issue is fixed by https://github.com/socketio/socket.io/commit/15af22fc22bc6030fcead322c106f07640336115, included in socket.io@4.6.2
(released in May 2023).
The fix was backported in the 2.x branch today: https://github.com/socketio/socket.io/commit/d30630ba10562bf987f4d2b42440fc41a828119c
As a workaround for the affected versions of the socket.io
package, you can attach a listener for the "error" event:
io.on("connection", (socket) => {
socket.on("error", () => {
// ...
});
});
If you have any questions or comments about this advisory:
Thanks a lot to Paul Taylor for the responsible disclosure.
Resource exhaustion in engine.io
Engine.IO before 4.0.0 and 3.6.0 allows attackers to cause a denial of service (resource consumption) via a POST request to the long polling transport.
Uncaught exception in engine.io
A specially crafted HTTP request can trigger an uncaught exception on the Engine.IO server, thus killing the Node.js process.
events.js:292
throw er; // Unhandled 'error' event
^
Error: read ECONNRESET
at TCP.onStreamRead (internal/stream_base_commons.js:209:20)
Emitted 'error' event on Socket instance at:
at emitErrorNT (internal/streams/destroy.js:106:8)
at emitErrorCloseNT (internal/streams/destroy.js:74:3)
at processTicksAndRejections (internal/process/task_queues.js:80:21) {
errno: -104,
code: 'ECONNRESET',
syscall: 'read'
}
This impacts all the users of the engine.io
package, including those who uses depending packages like socket.io
.
A fix has been released today (2022/11/20):
| Version range | Fixed version |
|-------------------|---------------|
| engine.io@3.x.y
| 3.6.1
|
| engine.io@6.x.y
| 6.2.1
|
For socket.io
users:
| Version range | engine.io
version | Needs minor update? |
|-----------------------------|---------------------|--------------------------------------------------------------------------------------------------------|
| socket.io@4.5.x
| ~6.2.0
| npm audit fix
should be sufficient |
| socket.io@4.4.x
| ~6.1.0
| Please upgrade to socket.io@4.5.x
|
| socket.io@4.3.x
| ~6.0.0
| Please upgrade to socket.io@4.5.x
|
| socket.io@4.2.x
| ~5.2.0
| Please upgrade to socket.io@4.5.x
|
| socket.io@4.1.x
| ~5.1.1
| Please upgrade to socket.io@4.5.x
|
| socket.io@4.0.x
| ~5.0.0
| Please upgrade to socket.io@4.5.x
|
| socket.io@3.1.x
| ~4.1.0
| Please upgrade to socket.io@4.5.x
(see here) |
| socket.io@3.0.x
| ~4.0.0
| Please upgrade to socket.io@4.5.x
(see here) |
| socket.io@2.5.0
| ~3.6.0
| npm audit fix
should be sufficient |
| socket.io@2.4.x
and below | ~3.5.0
| Please upgrade to socket.io@2.5.0
|
There is no known workaround except upgrading to a safe version.
If you have any questions or comments about this advisory:
engine.io
Thanks to Jonathan Neve for the responsible disclosure.
ws affected by a DoS when handling a request with many HTTP headers
A request with a number of headers exceeding theserver.maxHeadersCount
threshold could be used to crash a ws server.
const http = require('http');
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 0 }, function () {
const chars = "!#$%&'*+-.0123456789abcdefghijklmnopqrstuvwxyz^_`|~".split('');
const headers = {};
let count = 0;
for (let i = 0; i < chars.length; i++) {
if (count === 2000) break;
for (let j = 0; j < chars.length; j++) {
const key = chars[i] + chars[j];
headers[key] = 'x';
if (++count === 2000) break;
}
}
headers.Connection = 'Upgrade';
headers.Upgrade = 'websocket';
headers['Sec-WebSocket-Key'] = 'dGhlIHNhbXBsZSBub25jZQ==';
headers['Sec-WebSocket-Version'] = '13';
const request = http.request({
headers: headers,
host: '127.0.0.1',
port: wss.address().port
});
request.end();
});
The vulnerability was fixed in ws@8.17.1 (https://github.com/websockets/ws/commit/e55e5106f10fcbaac37cfa89759e4cc0d073a52c) and backported to ws@7.5.10 (https://github.com/websockets/ws/commit/22c28763234aa75a7e1b76f5c01c181260d7917f), ws@6.2.3 (https://github.com/websockets/ws/commit/eeb76d313e2a00dd5247ca3597bba7877d064a63), and ws@5.2.4 (https://github.com/websockets/ws/commit/4abd8f6de4b0b65ef80b3ff081989479ed93377e)
In vulnerable versions of ws, the issue can be mitigated in the following ways:
--max-http-header-size=size
and/or the maxHeaderSize
options so that no more headers than the server.maxHeadersCount
limit can be sent.server.maxHeadersCount
to 0
so that no limit is applied.The vulnerability was reported by Ryan LaPointe in https://github.com/websockets/ws/issues/2230.
cookie accepts cookie name, path, and domain with out of bounds characters
The cookie name could be used to set other fields of the cookie, resulting in an unexpected cookie value. For example, serialize("userName=<script>alert('XSS3')</script>; Max-Age=2592000; a", value)
would result in "userName=<script>alert('XSS3')</script>; Max-Age=2592000; a=test"
, setting userName
cookie to <script>
and ignoring value
.
A similar escape can be used for path
and domain
, which could be abused to alter other fields of the cookie.
Upgrade to 0.7.0, which updates the validation for name
, path
, and domain
.
Avoid passing untrusted or arbitrary values for these fields, ensure they are set by the application instead of user input.
parse-uri Regular expression Denial of Service (ReDoS)
An issue in parse-uri v1.0.9 allows attackers to cause a Regular expression Denial of Service (ReDoS) via a crafted URL.
async function exploit() {
const parseuri = require("parse-uri");
// This input is designed to cause excessive backtracking in the regex
const craftedInput = 'http://example.com/' + 'a'.repeat(30000) + '?key=value';
const result = await parseuri(craftedInput);
}
await exploit();
Improper Certificate Validation in xmlhttprequest-ssl
The xmlhttprequest-ssl package before 1.6.1 for Node.js disables SSL certificate validation by default, because rejectUnauthorized (when the property exists but is undefined) is considered to be false within the https.request function of Node.js. In other words, no certificate is ever rejected.
xmlhttprequest and xmlhttprequest-ssl vulnerable to Arbitrary Code Injection
This affects the package xmlhttprequest before 1.7.0; all versions of package xmlhttprequest-ssl. Provided requests are sent synchronously (async=False
on xhr.open
), malicious user input flowing into xhr.send
could result in arbitrary code being injected and run.
Resource exhaustion in socket.io-parser
The socket.io-parser
npm package before versions 3.3.2 and 3.4.1 allows attackers to cause a denial of service (memory consumption) via a large packet because a concatenation approach is used.
Insufficient validation when decoding a Socket.IO packet
Due to improper type validation in the socket.io-parser
library (which is used by the socket.io
and socket.io-client
packages to encode and decode Socket.IO packets), it is possible to overwrite the _placeholder object which allows an attacker to place references to functions at arbitrary places in the resulting query object.
Example:
const decoder = new Decoder();
decoder.on("decoded", (packet) => {
console.log(packet.data); // prints [ 'hello', [Function: splice] ]
})
decoder.add('51-["hello",{"_placeholder":true,"num":"splice"}]');
decoder.add(Buffer.from("world"));
This bubbles up in the socket.io
package:
io.on("connection", (socket) => {
socket.on("hello", (val) => {
// here, "val" could be a function instead of a buffer
});
});
:warning: IMPORTANT NOTE :warning:
You need to make sure that the payload that you received from the client is actually a Buffer
object:
io.on("connection", (socket) => {
socket.on("hello", (val) => {
if (!Buffer.isBuffer(val)) {
socket.disconnect();
return;
}
// ...
});
});
If that's already the case, then you are not impacted by this issue, and there is no way an attacker could make your server crash (or escalate privileges, ...).
Example of values that could be sent by a malicious user:
Sample packet: 451-["hello",{"_placeholder":true,"num":10}]
io.on("connection", (socket) => {
socket.on("hello", (val) => {
// val is `undefined`
});
});
undefined
Sample packet: 451-["hello",{"_placeholder":true,"num":undefined}]
io.on("connection", (socket) => {
socket.on("hello", (val) => {
// val is `undefined`
});
});
Array
, like "push"Sample packet: 451-["hello",{"_placeholder":true,"num":"push"}]
io.on("connection", (socket) => {
socket.on("hello", (val) => {
// val is a reference to the "push" function
});
});
Object
, like "hasOwnProperty"Sample packet: 451-["hello",{"_placeholder":true,"num":"hasOwnProperty"}]
io.on("connection", (socket) => {
socket.on("hello", (val) => {
// val is a reference to the "hasOwnProperty" function
});
});
This should be fixed by:
socket.io-parser@4.2.1
socket.io-parser@4.0.5
socket.io-parser@3.4.2
socket.io-parser@3.3.3
socket.io
package| socket.io
version | socket.io-parser
version | Covered? |
|---------------------|---------------------------------------------------------------------------------------------------------|------------------------|
| 4.5.2...latest
| ~4.2.0
(ref) | Yes :heavy_check_mark: |
| 4.1.3...4.5.1
| ~4.0.4
(ref) | Yes :heavy_check_mark: |
| 3.0.5...4.1.2
| ~4.0.3
(ref) | Yes :heavy_check_mark: |
| 3.0.0...3.0.4
| ~4.0.1
(ref) | Yes :heavy_check_mark: |
| 2.3.0...2.5.0
| ~3.4.0
(ref) | Yes :heavy_check_mark: |
socket.io-client
package| socket.io-client
version | socket.io-parser
version | Covered? |
|----------------------------|----------------------------------------------------------------------------------------------------------------|------------------------------------|
| 4.5.0...latest
| ~4.2.0
(ref) | Yes :heavy_check_mark: |
| 4.3.0...4.4.1
| ~4.1.1
(ref) | No, but the impact is very limited |
| 3.1.0...4.2.0
| ~4.0.4
(ref) | Yes :heavy_check_mark: |
| 3.0.5
| ~4.0.3
(ref) | Yes :heavy_check_mark: |
| 3.0.0...3.0.4
| ~4.0.1
(ref) | Yes :heavy_check_mark: |
| 2.2.0...2.5.0
| ~3.3.0
(ref) | Yes :heavy_check_mark: |
Insufficient validation when decoding a Socket.IO packet
A specially crafted Socket.IO packet can trigger an uncaught exception on the Socket.IO server, thus killing the Node.js process.
TypeError: Cannot convert object to primitive value
at Socket.emit (node:events:507:25)
at .../node_modules/socket.io/lib/socket.js:531:14
A fix has been released today (2023/05/22):
socket.io-parser@4.2.3
socket.io-parser@3.4.3
Another fix has been released for the 3.3.x
branch:
| socket.io
version | socket.io-parser
version | Needs minor update? |
|---------------------|---------------------------------------------------------------------------------------------------------|--------------------------------------|
| 4.5.2...latest
| ~4.2.0
(ref) | npm audit fix
should be sufficient |
| 4.1.3...4.5.1
| ~4.1.1
(ref) | Please upgrade to socket.io@4.6.x
|
| 3.0.5...4.1.2
| ~4.0.3
(ref) | Please upgrade to socket.io@4.6.x
|
| 3.0.0...3.0.4
| ~4.0.1
(ref) | Please upgrade to socket.io@4.6.x
|
| 2.3.0...2.5.0
| ~3.4.0
(ref) | npm audit fix
should be sufficient |
There is no known workaround except upgrading to a safe version.
If you have any questions or comments about this advisory:
Thanks to @rafax00 for the responsible disclosure.