Skip to content

Commit f78a575

Browse files
fix(security): do not allow all origins by default
BREAKING CHANGE: previously, all origins were allowed by default, which meant that a Socket.IO server sent the necessary CORS headers (`Access-Control-Allow-xxx`) to any domain by default. Please note that you are not impacted if: - you are using Socket.IO v2 and the `origins` option to restrict the list of allowed domains - you are using Socket.IO v3 (disabled by default) This commit also removes the support for '*' matchers and protocol-less URL: ``` io.origins('https://example.com:443'); => io.origins(['https://example.com']); io.origins('localhost:3000'); => io.origins(['http://localhost:3000']); io.origins('http://localhost:*'); => io.origins(['http://localhost:3000']); io.origins('*:3000'); => io.origins(['http://localhost:3000']); ``` To restore the previous behavior (please use with caution): ```js io.origins((_, callback) => { callback(null, true); }); ``` See also: - https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS - https://socket.io/docs/v3/handling-cors/ - https://socket.io/docs/v3/migrating-from-2-x-to-3-0/#CORS-handling Thanks a lot to https://github.com/ni8walk3r for the security report.
1 parent d33a619 commit f78a575

File tree

2 files changed

+23
-47
lines changed

2 files changed

+23
-47
lines changed

lib/index.js

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function Server(srv, opts){
5454
this.parser = opts.parser || parser;
5555
this.encoder = new this.parser.Encoder();
5656
this.adapter(opts.adapter || Adapter);
57-
this.origins(opts.origins || '*:*');
57+
this.origins(opts.origins || []);
5858
this.sockets = this.of('/');
5959
if (srv) this.attach(srv, opts);
6060
}
@@ -67,31 +67,18 @@ function Server(srv, opts){
6767
*/
6868

6969
Server.prototype.checkRequest = function(req, fn) {
70-
var origin = req.headers.origin || req.headers.referer;
70+
const origin = req.headers.origin;
7171

72-
// file:// URLs produce a null Origin which can't be authorized via echo-back
73-
if ('null' == origin || null == origin) origin = '*';
72+
if (typeof this._origins === 'function') {
73+
return this._origins(origin, fn);
74+
}
7475

75-
if (!!origin && typeof(this._origins) == 'function') return this._origins(origin, fn);
76-
if (this._origins.indexOf('*:*') !== -1) return fn(null, true);
7776
if (origin) {
78-
try {
79-
var parts = url.parse(origin);
80-
var defaultPort = 'https:' == parts.protocol ? 443 : 80;
81-
parts.port = parts.port != null
82-
? parts.port
83-
: defaultPort;
84-
var ok =
85-
~this._origins.indexOf(parts.protocol + '//' + parts.hostname + ':' + parts.port) ||
86-
~this._origins.indexOf(parts.hostname + ':' + parts.port) ||
87-
~this._origins.indexOf(parts.hostname + ':*') ||
88-
~this._origins.indexOf('*:' + parts.port);
89-
debug('origin %s is %svalid', origin, !!ok ? '' : 'not ');
90-
return fn(null, !!ok);
91-
} catch (ex) {
92-
}
77+
fn(null, this._origins.includes(origin));
78+
} else {
79+
const noOriginIsValid = this._origins.length === 0;
80+
fn(null, noOriginIsValid);
9381
}
94-
fn(null, false);
9582
};
9683

9784
/**
@@ -237,7 +224,7 @@ Server.prototype.adapter = function(v){
237224
Server.prototype.origins = function(v){
238225
if (!arguments.length) return this._origins;
239226

240-
this._origins = v;
227+
this._origins = typeof v === 'string' ? [v] : v;
241228
return this;
242229
};
243230

test/socket.io.js

Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('socket.io', function(){
7373
it('should be able to set origins to engine.io', function() {
7474
var srv = io(http());
7575
srv.set('origins', 'http://hostname.com:*');
76-
expect(srv.origins()).to.be('http://hostname.com:*');
76+
expect(srv.origins()).to.eql(['http://hostname.com:*']);
7777
});
7878

7979
it('should be able to set authorization and send error packet', function(done) {
@@ -262,17 +262,6 @@ describe('socket.io', function(){
262262
});
263263
});
264264

265-
it('should allow request when origin defined an the same is specified', function(done) {
266-
var sockets = io({ origins: 'http://foo.example:*' }).listen('54015');
267-
request.get('http://localhost:54015/socket.io/default/')
268-
.set('origin', 'http://foo.example')
269-
.query({ transport: 'polling' })
270-
.end(function (err, res) {
271-
expect(res.status).to.be(200);
272-
done();
273-
});
274-
});
275-
276265
it('should allow request when origin defined as function and same is supplied', function(done) {
277266
var sockets = io({ origins: function(origin,callback){
278267
if (origin == 'http://foo.example') {
@@ -307,7 +296,7 @@ describe('socket.io', function(){
307296

308297
it('should allow request when origin defined as function and no origin is supplied', function(done) {
309298
var sockets = io({ origins: function(origin,callback){
310-
if (origin == '*') {
299+
if (origin === undefined) {
311300
return callback(null, true);
312301
}
313302
return callback(null, false);
@@ -320,17 +309,6 @@ describe('socket.io', function(){
320309
});
321310
});
322311

323-
it('should default to port 443 when protocol is https', function(done) {
324-
var sockets = io({ origins: 'https://foo.example:443' }).listen('54036');
325-
request.get('http://localhost:54036/socket.io/default/')
326-
.set('origin', 'https://foo.example')
327-
.query({ transport: 'polling' })
328-
.end(function (err, res) {
329-
expect(res.status).to.be(200);
330-
done();
331-
});
332-
});
333-
334312
it('should allow request if custom function in opts.allowRequest returns true', function(done){
335313
var sockets = io(http().listen(54022), { allowRequest: function (req, callback) {
336314
return callback(null, true);
@@ -367,6 +345,17 @@ describe('socket.io', function(){
367345
done();
368346
});
369347
});
348+
349+
it('should disallow any origin by default', (done) => {
350+
io().listen('54025');
351+
request.get('http://localhost:54025/socket.io/default/')
352+
.set('origin', 'https://foo.example')
353+
.query({ transport: 'polling' })
354+
.end((err, res) => {
355+
expect(res.status).to.be(403);
356+
done();
357+
});
358+
});
370359
});
371360

372361
describe('close', function(){

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy