Skip to content

Commit

Permalink
debugger: validate sec-websocket-accept response header
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
copperwall authored and Trott committed Jul 11, 2021
1 parent f2f6872 commit 2387c02
Showing 1 changed file with 22 additions and 4 deletions.
26 changes: 22 additions & 4 deletions lib/internal/debugger/inspect_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const {
} = primordials;

const Buffer = require('buffer').Buffer;
const crypto = require('crypto');
const { ERR_DEBUGGER_ERROR } = require('internal/errors').codes;
const { EventEmitter } = require('events');
const http = require('http');
Expand All @@ -35,13 +36,30 @@ 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 unpackError({ code, message, data }) {
const err = new ERR_DEBUGGER_ERROR(`${message} - ${data}`);
err.code = code;
ErrorCaptureStackTrace(err, unpackError);
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 ERR_DEBUGGER_ERROR(
`Websocket secret mismatch: ${requestKey} did not match ${responseKey}`
);
}
}

function encodeFrameHybi17(payload) {
var i;

Expand Down Expand Up @@ -287,8 +305,8 @@ class Client extends EventEmitter {
_connectWebsocket(urlPath) {
this.reset();

const key1 = require('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,
Expand All @@ -297,7 +315,7 @@ class Client extends EventEmitter {
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Sec-WebSocket-Key': key1,
'Sec-WebSocket-Key': requestKey,
'Sec-WebSocket-Version': '13',
},
});
Expand All @@ -314,7 +332,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;
Expand Down

0 comments on commit 2387c02

Please sign in to comment.