mirror of
https://github.com/socketio/socket.io.git
synced 2026-01-10 23:48:02 -05:00
feat: disable cookie by default and add sameSite attribute
The cookie might be used for sticky-session, but is not mandatory so it
makes sense to disable it by default.
The change also add a SameSite=Lax attribute by default.
Breaking change: the syntax has changed from
```
new Server({
cookieName: "test",
cookieHttpOnly: false,
cookiePath: "/custom"
})
```
to
```
new Server({
cookie: {
name: "test",
httpOnly: false,
path: "/custom"
}
})
```
All other options (domain, maxAge, sameSite, ...) are now supported.
Reference: https://github.com/jshttp/cookie#options-1
This commit is contained in:
@@ -244,13 +244,10 @@ to a single process.
|
||||
- `httpCompression` (`Object|Boolean`): parameters of the http compression for the polling transports
|
||||
(see [zlib](http://nodejs.org/api/zlib.html#zlib_options) api docs). Set to `false` to disable. (`true`)
|
||||
- `threshold` (`Number`): data is compressed only if the byte size is above this value (`1024`)
|
||||
- `cookie` (`String|Boolean`): name of the HTTP cookie that
|
||||
- `cookie` (`Object|Boolean`): configuration of the cookie that
|
||||
contains the client sid to send as part of handshake response
|
||||
headers. Set to `false` to not send one. (`io`)
|
||||
- `cookiePath` (`String|Boolean`): path of the above `cookie`
|
||||
option. If false, no path will be sent, which means browsers will only send the cookie on the engine.io attached path (`/engine.io`).
|
||||
Set false to not save io cookie on all requests. (`/`)
|
||||
- `cookieHttpOnly` (`Boolean`): If `true` HttpOnly io cookie cannot be accessed by client-side APIs, such as JavaScript. (`true`) _This option has no effect if `cookie` or `cookiePath` is set to `false`._
|
||||
headers. This cookie might be used for sticky-session. Defaults to not sending any cookie (`false`).
|
||||
See [here](https://github.com/jshttp/cookie#options-1) for all supported options.
|
||||
- `wsEngine` (`String`): what WebSocket server implementation to use. Specified module must conform to the `ws` interface (see [ws module api docs](https://github.com/websockets/ws/blob/master/doc/ws.md)). Default value is `ws`. An alternative c++ addon is also available by installing `uws` module.
|
||||
- `initialPacket` (`Object`): an optional packet which will be concatenated to the handshake packet emitted by Engine.IO.
|
||||
- `close`
|
||||
|
||||
@@ -30,16 +30,26 @@ class Server extends EventEmitter {
|
||||
this.transports = opts.transports || Object.keys(transports);
|
||||
this.allowUpgrades = false !== opts.allowUpgrades;
|
||||
this.allowRequest = opts.allowRequest;
|
||||
this.cookie = false !== opts.cookie ? opts.cookie || "io" : false;
|
||||
this.cookiePath =
|
||||
false !== opts.cookiePath ? opts.cookiePath || "/" : false;
|
||||
this.cookieHttpOnly = false !== opts.cookieHttpOnly;
|
||||
this.perMessageDeflate =
|
||||
false !== opts.perMessageDeflate ? opts.perMessageDeflate || true : false;
|
||||
this.httpCompression =
|
||||
false !== opts.httpCompression ? opts.httpCompression || {} : false;
|
||||
this.initialPacket = opts.initialPacket;
|
||||
|
||||
this.opts = Object.assign({}, opts);
|
||||
|
||||
if (opts.cookie) {
|
||||
this.opts.cookie = Object.assign(
|
||||
{
|
||||
name: "io",
|
||||
path: "/",
|
||||
httpOnly: opts.cookie.path !== false,
|
||||
sameSite: "lax"
|
||||
},
|
||||
opts.cookie
|
||||
);
|
||||
}
|
||||
|
||||
// initialize compression options
|
||||
["perMessageDeflate", "httpCompression"].forEach(type => {
|
||||
let compression = this[type];
|
||||
@@ -249,12 +259,13 @@ class Server extends EventEmitter {
|
||||
const socket = new Socket(id, this, transport, req);
|
||||
const self = this;
|
||||
|
||||
if (false !== this.cookie) {
|
||||
transport.on("headers", function(headers) {
|
||||
headers["Set-Cookie"] = cookieMod.serialize(self.cookie, id, {
|
||||
path: self.cookiePath,
|
||||
httpOnly: self.cookiePath ? self.cookieHttpOnly : false
|
||||
});
|
||||
if (this.opts.cookie) {
|
||||
transport.on("headers", headers => {
|
||||
headers["Set-Cookie"] = cookieMod.serialize(
|
||||
this.opts.cookie.name,
|
||||
id,
|
||||
this.opts.cookie
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
136
test/server.js
136
test/server.js
@@ -141,133 +141,137 @@ describe("server", function() {
|
||||
});
|
||||
|
||||
describe("handshake", function() {
|
||||
it("should send the io cookie", function(done) {
|
||||
listen(function(port) {
|
||||
it("should send the io cookie", done => {
|
||||
listen({ cookie: true }, port => {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
// hack-obtain sid
|
||||
var sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
const sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be(
|
||||
"io=" + sid + "; Path=/; HttpOnly"
|
||||
`io=${sid}; Path=/; HttpOnly; SameSite=Lax`
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send the io cookie custom name", function(done) {
|
||||
listen({ cookie: "woot" }, function(port) {
|
||||
it("should send the io cookie custom name", done => {
|
||||
listen({ cookie: { name: "woot" } }, port => {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
var sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
const sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be(
|
||||
"woot=" + sid + "; Path=/; HttpOnly"
|
||||
`woot=${sid}; Path=/; HttpOnly; SameSite=Lax`
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send the cookie with custom path", function(done) {
|
||||
listen({ cookiePath: "/custom" }, function(port) {
|
||||
it("should send the cookie with custom path", done => {
|
||||
listen({ cookie: { path: "/custom" } }, port => {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
var sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
const sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be(
|
||||
"io=" + sid + "; Path=/custom; HttpOnly"
|
||||
`io=${sid}; Path=/custom; HttpOnly; SameSite=Lax`
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send the cookie with path=false", function(done) {
|
||||
listen({ cookiePath: false }, function(port) {
|
||||
it("should send the cookie with path=false", done => {
|
||||
listen({ cookie: { path: false } }, port => {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
var sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be("io=" + sid);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send the io cookie with httpOnly=true", function(done) {
|
||||
listen({ cookieHttpOnly: true }, function(port) {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
var sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
const sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be(
|
||||
"io=" + sid + "; Path=/; HttpOnly"
|
||||
`io=${sid}; SameSite=Lax`
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send the io cookie with httpOnly=true and path=false", function(done) {
|
||||
listen({ cookieHttpOnly: true, cookiePath: false }, function(port) {
|
||||
it("should send the io cookie with httpOnly=true", done => {
|
||||
listen({ cookie: { httpOnly: true } }, port => {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
var sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be("io=" + sid);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send the io cookie with httpOnly=false", function(done) {
|
||||
listen({ cookieHttpOnly: false }, function(port) {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
var sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
const sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be(
|
||||
"io=" + sid + "; Path=/"
|
||||
`io=${sid}; Path=/; HttpOnly; SameSite=Lax`
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send the io cookie with httpOnly not boolean", function(done) {
|
||||
listen({ cookieHttpOnly: "no" }, function(port) {
|
||||
it("should send the io cookie with sameSite=strict", done => {
|
||||
listen({ cookie: { sameSite: "strict" } }, port => {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
var sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
const sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be(
|
||||
"io=" + sid + "; Path=/; HttpOnly"
|
||||
`io=${sid}; Path=/; HttpOnly; SameSite=Strict`
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should not send the io cookie", function(done) {
|
||||
listen({ cookie: false }, function(port) {
|
||||
it("should send the io cookie with httpOnly=false", done => {
|
||||
listen({ cookie: { httpOnly: false } }, port => {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
const sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be(
|
||||
`io=${sid}; Path=/; SameSite=Lax`
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should send the io cookie with httpOnly not boolean", done => {
|
||||
listen({ cookie: { httpOnly: "no" } }, port => {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling", b64: 1 })
|
||||
.end(function(err, res) {
|
||||
expect(err).to.be(null);
|
||||
const sid = res.text.match(/"sid":"([^"]+)"/)[1];
|
||||
expect(res.headers["set-cookie"][0]).to.be(
|
||||
`io=${sid}; Path=/; HttpOnly; SameSite=Lax`
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should not send the io cookie", done => {
|
||||
listen({ cookie: false }, port => {
|
||||
request
|
||||
.get("http://localhost:%d/engine.io/default/".s(port))
|
||||
.query({ transport: "polling" })
|
||||
@@ -2549,7 +2553,9 @@ describe("server", function() {
|
||||
}
|
||||
|
||||
it("should compress by default", function(done) {
|
||||
var engine = listen({ transports: ["polling"] }, function(port) {
|
||||
var engine = listen({ cookie: true, transports: ["polling"] }, function(
|
||||
port
|
||||
) {
|
||||
engine.on("connection", function(conn) {
|
||||
var buf = Buffer.allocUnsafe(1024);
|
||||
for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff;
|
||||
@@ -2584,7 +2590,9 @@ describe("server", function() {
|
||||
});
|
||||
|
||||
it("should compress using deflate", function(done) {
|
||||
var engine = listen({ transports: ["polling"] }, function(port) {
|
||||
var engine = listen({ cookie: true, transports: ["polling"] }, function(
|
||||
port
|
||||
) {
|
||||
engine.on("connection", function(conn) {
|
||||
var buf = Buffer.allocUnsafe(1024);
|
||||
for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff;
|
||||
@@ -2620,7 +2628,11 @@ describe("server", function() {
|
||||
|
||||
it("should set threshold", function(done) {
|
||||
var engine = listen(
|
||||
{ transports: ["polling"], httpCompression: { threshold: 0 } },
|
||||
{
|
||||
cookie: true,
|
||||
transports: ["polling"],
|
||||
httpCompression: { threshold: 0 }
|
||||
},
|
||||
function(port) {
|
||||
engine.on("connection", function(conn) {
|
||||
var buf = Buffer.allocUnsafe(10);
|
||||
@@ -2654,7 +2666,7 @@ describe("server", function() {
|
||||
|
||||
it("should disable compression", function(done) {
|
||||
var engine = listen(
|
||||
{ transports: ["polling"], httpCompression: false },
|
||||
{ cookie: true, transports: ["polling"], httpCompression: false },
|
||||
function(port) {
|
||||
engine.on("connection", function(conn) {
|
||||
var buf = Buffer.allocUnsafe(1024);
|
||||
@@ -2687,7 +2699,9 @@ describe("server", function() {
|
||||
});
|
||||
|
||||
it("should disable compression per message", function(done) {
|
||||
var engine = listen({ transports: ["polling"] }, function(port) {
|
||||
var engine = listen({ cookie: true, transports: ["polling"] }, function(
|
||||
port
|
||||
) {
|
||||
engine.on("connection", function(conn) {
|
||||
var buf = Buffer.allocUnsafe(1024);
|
||||
for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff;
|
||||
@@ -2718,7 +2732,9 @@ describe("server", function() {
|
||||
});
|
||||
|
||||
it("should not compress when the byte size is below threshold", function(done) {
|
||||
var engine = listen({ transports: ["polling"] }, function(port) {
|
||||
var engine = listen({ cookie: true, transports: ["polling"] }, function(
|
||||
port
|
||||
) {
|
||||
engine.on("connection", function(conn) {
|
||||
var buf = Buffer.allocUnsafe(100);
|
||||
for (var i = 0; i < buf.length; i++) buf[i] = i % 0xff;
|
||||
|
||||
Reference in New Issue
Block a user