Skip to content

Commit f78a575

Browse files
committedJan 4, 2021
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

+10-23
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

+13-24
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)
Please sign in to comment.