Skip to content

Commit 80184dd

Browse files
committed
Harden Node.js WebSocket server handling
Client: nodejs - Validate origin on WebSocket upgrade using the same CORS rules as HTTP requests - Fix path containment check to use trailing separator - Replace deprecated new Buffer() with Buffer.alloc() - Sanitize interpolated header values in upgrade response (04) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> This closes #3391
1 parent 158edaa commit 80184dd

1 file changed

Lines changed: 25 additions & 9 deletions

File tree

lib/nodejs/lib/thrift/web_server.js

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ var log = require("./log");
2727
var MultiplexedProcessor =
2828
require("./multiplexed_processor").MultiplexedProcessor;
2929

30+
function sanitizeHeader(value) {
31+
return (value || "").replace(/[\r\n]/g, "");
32+
}
33+
3034
var TBufferedTransport = require("./buffered_transport");
3135
var TBinaryProtocol = require("./binary_protocol");
3236
var InputBufferUnderrunError = require("./input_buffer_underrun_error");
@@ -84,7 +88,7 @@ var wsFrame = {
8488
* @returns {Buffer} - The WebSocket frame, ready to send
8589
*/
8690
encode: function (data, mask, binEncoding) {
87-
var frame = new Buffer(wsFrame.frameSizeFromData(data, mask));
91+
var frame = Buffer.alloc(wsFrame.frameSizeFromData(data, mask));
8892
//Byte 0 - FIN & OPCODE
8993
frame[0] =
9094
wsFrame.fin.FIN +
@@ -171,19 +175,19 @@ var wsFrame = {
171175
}
172176
//MASK
173177
if (wsFrame.mask.TO_SERVER == (frame[1] & wsFrame.mask.TO_SERVER)) {
174-
result.mask = new Buffer(4);
178+
result.mask = Buffer.alloc(4);
175179
frame.copy(result.mask, 0, dataOffset, dataOffset + 4);
176180
dataOffset += 4;
177181
}
178182
//Payload
179-
result.data = new Buffer(len);
183+
result.data = Buffer.alloc(len);
180184
frame.copy(result.data, 0, dataOffset, dataOffset + len);
181185
if (result.mask) {
182186
wsFrame.applyMask(result.data, result.mask);
183187
}
184188
//Next Frame
185189
if (frame.length > dataOffset + len) {
186-
result.nextFrame = new Buffer(frame.length - (dataOffset + len));
190+
result.nextFrame = Buffer.alloc(frame.length - (dataOffset + len));
187191
frame.copy(result.nextFrame, 0, dataOffset + len, frame.length);
188192
}
189193
//Don't forward control frames
@@ -450,7 +454,8 @@ exports.createWebServer = function (options) {
450454
var filename = path.resolve(path.join(baseDir, uri));
451455

452456
//Ensure the basedir path is not able to be escaped
453-
if (filename.indexOf(baseDir) != 0) {
457+
var normalizedBase = baseDir.endsWith(path.sep) ? baseDir : baseDir + path.sep;
458+
if (filename !== baseDir && filename.indexOf(normalizedBase) !== 0) {
454459
response.writeHead(400, "Invalid request path", {});
455460
response.end();
456461
return;
@@ -543,6 +548,14 @@ exports.createWebServer = function (options) {
543548
}
544549
})
545550
.on("upgrade", function (request, socket, head) {
551+
//Verify CORS origin for WebSocket upgrades
552+
if (request.headers.origin && options.cors) {
553+
if (!options.cors["*"] && !options.cors[request.headers.origin]) {
554+
socket.write("HTTP/1.1 403 Origin not allowed\r\n\r\n");
555+
socket.destroy();
556+
return;
557+
}
558+
}
546559
//Lookup service
547560
var svc;
548561
try {
@@ -557,6 +570,9 @@ exports.createWebServer = function (options) {
557570
request.headers["sec-websocket-key"] +
558571
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
559572
);
573+
var origin = sanitizeHeader(request.headers.origin);
574+
var host = sanitizeHeader(request.headers.host);
575+
var reqUrl = sanitizeHeader(request.url);
560576
socket.write(
561577
"HTTP/1.1 101 Switching Protocols\r\n" +
562578
"Upgrade: websocket\r\n" +
@@ -565,11 +581,11 @@ exports.createWebServer = function (options) {
565581
hash.digest("base64") +
566582
"\r\n" +
567583
"Sec-WebSocket-Origin: " +
568-
request.headers.origin +
584+
origin +
569585
"\r\n" +
570586
"Sec-WebSocket-Location: ws://" +
571-
request.headers.host +
572-
request.url +
587+
host +
588+
reqUrl +
573589
"\r\n" +
574590
"\r\n",
575591
);
@@ -582,7 +598,7 @@ exports.createWebServer = function (options) {
582598
//Prepend any existing decoded data
583599
if (data) {
584600
if (result.data) {
585-
var newData = new Buffer(data.length + result.data.length);
601+
var newData = Buffer.alloc(data.length + result.data.length);
586602
data.copy(newData);
587603
result.data.copy(newData, data.length);
588604
result.data = newData;

0 commit comments

Comments
 (0)