Skip to content

Commit 95db544

Browse files
copperwallBethGriggs
authored andcommitted
debugger: validate sec-websocket-accept response header
This addresses a TODO to validate that the sec-websocket-accept header in the WebSocket handshake response is valid. To do this we need to append the WebSocket GUID to the original key sent in sec-websocket-key, sha1 hash it, and then compare the base64 encoding with the value sent in the sec-websocket-accept response header. If they don't match, an error is thrown. PR-URL: #39357 Refs: nodejs/node-inspect#93 Reviewed-By: Colin Ihrig <[email protected]>
1 parent c6a7c3d commit 95db544

File tree

2 files changed

+23
-4
lines changed

2 files changed

+23
-4
lines changed

lib/internal/debugger/inspect_client.js

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const {
1111
} = primordials;
1212

1313
const Buffer = require('buffer').Buffer;
14+
const crypto = require('crypto');
1415
const { ERR_DEBUGGER_ERROR } = require('internal/errors').codes;
1516
const { EventEmitter } = require('events');
1617
const http = require('http');
@@ -35,13 +36,30 @@ const kTwoBytePayloadLengthField = 126;
3536
const kEightBytePayloadLengthField = 127;
3637
const kMaskingKeyWidthInBytes = 4;
3738

39+
// This guid is defined in the Websocket Protocol RFC
40+
// https://tools.ietf.org/html/rfc6455#section-1.3
41+
const WEBSOCKET_HANDSHAKE_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
42+
3843
function unpackError({ code, message, data }) {
3944
const err = new ERR_DEBUGGER_ERROR(`${message} - ${data}`);
4045
err.code = code;
4146
ErrorCaptureStackTrace(err, unpackError);
4247
return err;
4348
}
4449

50+
function validateHandshake(requestKey, responseKey) {
51+
const expectedResponseKeyBase = requestKey + WEBSOCKET_HANDSHAKE_GUID;
52+
const shasum = crypto.createHash('sha1');
53+
shasum.update(expectedResponseKeyBase);
54+
const shabuf = shasum.digest();
55+
56+
if (shabuf.toString('base64') !== responseKey) {
57+
throw new ERR_DEBUGGER_ERROR(
58+
`WebSocket secret mismatch: ${requestKey} did not match ${responseKey}`
59+
);
60+
}
61+
}
62+
4563
function encodeFrameHybi17(payload) {
4664
var i;
4765

@@ -287,8 +305,8 @@ class Client extends EventEmitter {
287305
_connectWebsocket(urlPath) {
288306
this.reset();
289307

290-
const key1 = require('crypto').randomBytes(16).toString('base64');
291-
debuglog('request websocket', key1);
308+
const requestKey = crypto.randomBytes(16).toString('base64');
309+
debuglog('request WebSocket', requestKey);
292310

293311
const httpReq = this._http = http.request({
294312
host: this._host,
@@ -297,7 +315,7 @@ class Client extends EventEmitter {
297315
headers: {
298316
'Connection': 'Upgrade',
299317
'Upgrade': 'websocket',
300-
'Sec-WebSocket-Key': key1,
318+
'Sec-WebSocket-Key': requestKey,
301319
'Sec-WebSocket-Version': '13',
302320
},
303321
});
@@ -314,7 +332,7 @@ class Client extends EventEmitter {
314332
});
315333

316334
const handshakeListener = (res, socket) => {
317-
// TODO: we *could* validate res.headers[sec-websocket-accept]
335+
validateHandshake(requestKey, res.headers['sec-websocket-accept']);
318336
debuglog('websocket upgrade');
319337

320338
this._socket = socket;

src/node_native_module.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ void NativeModuleLoader::InitializeModuleCategories() {
7070
std::vector<std::string> prefixes = {
7171
#if !HAVE_OPENSSL
7272
"internal/crypto/",
73+
"internal/debugger/",
7374
#endif // !HAVE_OPENSSL
7475

7576
"internal/bootstrap/",

0 commit comments

Comments
 (0)