Skip to content

Commit 482459e

Browse files
copperwalltargos
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 <cjihrig@gmail.com>
1 parent 43d28f5 commit 482459e

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

@@ -292,8 +310,8 @@ class Client extends EventEmitter {
292310
_connectWebsocket(urlPath) {
293311
this.reset();
294312

295-
const key1 = require('crypto').randomBytes(16).toString('base64');
296-
debuglog('request websocket', key1);
313+
const requestKey = crypto.randomBytes(16).toString('base64');
314+
debuglog('request WebSocket', requestKey);
297315

298316
const httpReq = this._http = http.request({
299317
host: this._host,
@@ -302,7 +320,7 @@ class Client extends EventEmitter {
302320
headers: {
303321
'Connection': 'Upgrade',
304322
'Upgrade': 'websocket',
305-
'Sec-WebSocket-Key': key1,
323+
'Sec-WebSocket-Key': requestKey,
306324
'Sec-WebSocket-Version': '13',
307325
},
308326
});
@@ -319,7 +337,7 @@ class Client extends EventEmitter {
319337
});
320338

321339
const handshakeListener = (res, socket) => {
322-
// TODO: we *could* validate res.headers[sec-websocket-accept]
340+
validateHandshake(requestKey, res.headers['sec-websocket-accept']);
323341
debuglog('websocket upgrade');
324342

325343
this._socket = socket;

src/node_native_module.cc

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

7677
"internal/bootstrap/",

0 commit comments

Comments
 (0)