diff --git a/lib/internal/inspect_client.js b/lib/internal/inspect_client.js index 9b8529d..2cffe04 100644 --- a/lib/internal/inspect_client.js +++ b/lib/internal/inspect_client.js @@ -46,6 +46,10 @@ const kTwoBytePayloadLengthField = 126; const kEightBytePayloadLengthField = 127; const kMaskingKeyWidthInBytes = 4; +// This guid is defined in the Websocket Protocol RFC +// https://tools.ietf.org/html/rfc6455#section-1.3 +const WEBSOCKET_HANDSHAKE_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; + function isEmpty(obj) { return Object.keys(obj).length === 0; } @@ -57,6 +61,19 @@ function unpackError({ code, message, data }) { return err; } +function validateHandshake(requestKey, responseKey) { + const expectedResponseKeyBase = requestKey + WEBSOCKET_HANDSHAKE_GUID; + const shasum = crypto.createHash('sha1'); + shasum.update(expectedResponseKeyBase); + const shabuf = shasum.digest(); + + if (shabuf.toString('base64') !== responseKey) { + throw new Error( + `Websocket secret mismatch: ${requestKey} did not match ${responseKey}` + ); + } +} + function encodeFrameHybi17(payload) { var i; @@ -300,8 +317,8 @@ class Client extends EventEmitter { _connectWebsocket(urlPath) { this.reset(); - const key1 = crypto.randomBytes(16).toString('base64'); - debuglog('request websocket', key1); + const requestKey = crypto.randomBytes(16).toString('base64'); + debuglog('request websocket', requestKey); const httpReq = this._http = http.request({ host: this._host, @@ -310,7 +327,7 @@ class Client extends EventEmitter { headers: { Connection: 'Upgrade', Upgrade: 'websocket', - 'Sec-WebSocket-Key': key1, + 'Sec-WebSocket-Key': requestKey, 'Sec-WebSocket-Version': '13', }, }); @@ -327,7 +344,7 @@ class Client extends EventEmitter { }); const handshakeListener = (res, socket) => { - // TODO: we *could* validate res.headers[sec-websocket-accept] + validateHandshake(requestKey, res.headers['sec-websocket-accept']); debuglog('websocket upgrade'); this._socket = socket;