Skip to content

Commit c42d0da

Browse files
timfjordevilebottnawi
authored andcommitted
fix: check origin header for websocket connection (#1626)
1 parent 2be7196 commit c42d0da

File tree

2 files changed

+40
-5
lines changed

2 files changed

+40
-5
lines changed

lib/Server.js

+27-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,22 @@ const webpackDevMiddleware = require('webpack-dev-middleware');
2424
const OptionsValidationError = require('./OptionsValidationError');
2525
const optionsSchema = require('./optionsSchema.json');
2626

27+
// Workaround for sockjs@~0.3.19
28+
// sockjs will remove Origin header, however Origin header is required for checking host.
29+
// See https://github.com/webpack/webpack-dev-server/issues/1604 for more information
30+
{
31+
// eslint-disable-next-line global-require
32+
const SockjsSession = require('sockjs/lib/transport').Session;
33+
const decorateConnection = SockjsSession.prototype.decorateConnection;
34+
SockjsSession.prototype.decorateConnection = function(req) {
35+
decorateConnection.call(this, req);
36+
const connection = this.connection;
37+
if (connection.headers && !('origin' in connection.headers) && 'origin' in req.headers) {
38+
connection.headers.origin = req.headers.origin;
39+
}
40+
};
41+
}
42+
2743
const clientStats = { errorDetails: false };
2844
const log = console.log; // eslint-disable-line no-console
2945

@@ -510,17 +526,23 @@ Server.prototype.setContentHeaders = function (req, res, next) {
510526
next();
511527
};
512528

513-
Server.prototype.checkHost = function (headers) {
529+
Server.prototype.checkHost = function (headers, headerToCheck) {
514530
// allow user to opt-out this security check, at own risk
515531
if (this.disableHostCheck) return true;
516532

533+
if (!headerToCheck) headerToCheck = 'host';
517534
// get the Host header and extract hostname
518535
// we don't care about port not matching
519-
const hostHeader = headers.host;
536+
const hostHeader = headers[headerToCheck];
520537
if (!hostHeader) return false;
521538

522539
// use the node url-parser to retrieve the hostname from the host-header.
523-
const hostname = url.parse(`//${hostHeader}`, false, true).hostname;
540+
const hostname = url.parse(
541+
// if hostHeader doesn't have scheme, add // for parsing.
542+
/^(.+:)?\/\//.test(hostHeader) ? hostHeader : `//${hostHeader}`,
543+
false,
544+
true,
545+
).hostname;
524546

525547
// always allow requests with explicit IPv4 or IPv6-address.
526548
// A note on IPv6 addresses: hostHeader will always contain the brackets denoting
@@ -581,8 +603,8 @@ Server.prototype.listen = function (port, hostname, fn) {
581603

582604
sockServer.on('connection', (conn) => {
583605
if (!conn) return;
584-
if (!this.checkHost(conn.headers)) {
585-
this.sockWrite([conn], 'error', 'Invalid Host header');
606+
if (!this.checkHost(conn.headers) || !this.checkHost(conn.headers, 'origin')) {
607+
this.sockWrite([conn], 'error', 'Invalid Host/Origin header');
586608
conn.close();
587609
return;
588610
}

test/Validation.test.js

+13
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ describe('Validation', () => {
144144
}
145145
});
146146

147+
it('should allow urls with scheme for checking origin', () => {
148+
const options = {
149+
public: 'test.host:80'
150+
};
151+
const headers = {
152+
origin: 'https://test.host'
153+
};
154+
const server = new Server(compiler, options);
155+
if (!server.checkHost(headers, 'origin')) {
156+
throw new Error("Validation didn't fail");
157+
}
158+
});
159+
147160
describe('allowedHosts', () => {
148161
it('should allow hosts in allowedHosts', () => {
149162
const testHosts = [

0 commit comments

Comments
 (0)