diff --git a/settings.json.docker b/settings.json.docker index 8bbdedb40..5e5d806fa 100644 --- a/settings.json.docker +++ b/settings.json.docker @@ -336,6 +336,24 @@ */ "trustProxy": "${TRUST_PROXY:false}", + /* + * Settings controlling the session cookie issued by Etherpad. + */ + "cookie": { + /* + * Value of the SameSite cookie property. "Lax" is recommended unless + * Etherpad will be embedded in an iframe from another site, in which case + * this must be set to "None". Note: "None" will not work (the browser will + * not send the cookie to Etherpad) unless https is used to access Etherpad + * (either directly or via a reverse proxy with "trustProxy" set to true). + * + * "Strict" is not recommended because it has few security benefits but + * significant usability drawbacks vs. "Lax". See + * https://stackoverflow.com/q/41841880 for discussion. + */ + "sameSite": "${COOKIE_SAME_SITE:Lax}" + }, + /* * Privacy: disable IP logging */ diff --git a/settings.json.template b/settings.json.template index 880310919..61d6db117 100644 --- a/settings.json.template +++ b/settings.json.template @@ -339,6 +339,24 @@ */ "trustProxy": false, + /* + * Settings controlling the session cookie issued by Etherpad. + */ + "cookie": { + /* + * Value of the SameSite cookie property. "Lax" is recommended unless + * Etherpad will be embedded in an iframe from another site, in which case + * this must be set to "None". Note: "None" will not work (the browser will + * not send the cookie to Etherpad) unless https is used to access Etherpad + * (either directly or via a reverse proxy with "trustProxy" set to true). + * + * "Strict" is not recommended because it has few security benefits but + * significant usability drawbacks vs. "Lax". See + * https://stackoverflow.com/q/41841880 for discussion. + */ + "sameSite": "Lax" + }, + /* * Privacy: disable IP logging */ diff --git a/src/node/hooks/express/webaccess.js b/src/node/hooks/express/webaccess.js index b43542ddc..744a6d316 100644 --- a/src/node/hooks/express/webaccess.js +++ b/src/node/hooks/express/webaccess.js @@ -237,9 +237,7 @@ exports.expressConfigure = (hook_name, args, cb) => { name: 'express_sid', proxy: true, cookie: { - // `Strict` is not used because it has few security benefits but significant usability - // drawbacks vs. `Lax`. See https://stackoverflow.com/q/41841880 for discussion. - sameSite: 'Lax', + sameSite: settings.cookie.sameSite, /* * The automatic express-session mechanism for determining if the * application is being served over ssl is similar to the one used for diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js index 6a03668c6..6f3ccea89 100644 --- a/src/node/utils/Settings.js +++ b/src/node/utils/Settings.js @@ -268,6 +268,24 @@ exports.sessionKey = false; */ exports.trustProxy = false; +/* + * Settings controlling the session cookie issued by Etherpad. + */ +exports.cookie = { + /* + * Value of the SameSite cookie property. "Lax" is recommended unless + * Etherpad will be embedded in an iframe from another site, in which case + * this must be set to "None". Note: "None" will not work (the browser will + * not send the cookie to Etherpad) unless https is used to access Etherpad + * (either directly or via a reverse proxy with "trustProxy" set to true). + * + * "Strict" is not recommended because it has few security benefits but + * significant usability drawbacks vs. "Lax". See + * https://stackoverflow.com/q/41841880 for discussion. + */ + sameSite: 'Lax', +}; + /* * This setting is used if you need authentication and/or * authorization. Note: /admin always requires authentication, and diff --git a/src/static/js/pad_utils.js b/src/static/js/pad_utils.js index 9e22951e1..c6620f077 100644 --- a/src/static/js/pad_utils.js +++ b/src/static/js/pad_utils.js @@ -528,13 +528,28 @@ padutils.setupGlobalExceptionHandler = setupGlobalExceptionHandler; padutils.binarySearch = require('./ace2_common').binarySearch; +// https://stackoverflow.com/a/42660748 +function inThirdPartyIframe() { + try { + return (!window.top.location.hostname); + } catch (e) { + return true; + } +} + // This file is included from Node so that it can reuse randomString, but Node doesn't have a global // window object. if (typeof window !== 'undefined') { exports.Cookies = require('js-cookie/src/js.cookie'); + // Use `SameSite=Lax`, unless Etherpad is embedded in an iframe from another site in which case + // use `SameSite=None`. For iframes from another site, only `None` has a chance of working + // because the cookies are third-party (not same-site). Many browsers/users block third-party + // cookies, but maybe blocked is better than definitely blocked (which would happen with `Lax` + // or `Strict`). Note: `None` will not work unless secure is true. + // // `Strict` is not used because it has few security benefits but significant usability drawbacks // vs. `Lax`. See https://stackoverflow.com/q/41841880 for discussion. - exports.Cookies.defaults.sameSite = 'Lax'; + exports.Cookies.defaults.sameSite = inThirdPartyIframe() ? 'None' : 'Lax'; exports.Cookies.defaults.secure = window.location.protocol === 'https:'; } exports.randomString = randomString;