mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* add SameSite:None support * add docs * Update docs/settings/security-settings.asciidoc Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com> Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
335d2a630b
commit
293d6de10c
4 changed files with 79 additions and 1 deletions
|
@ -50,6 +50,11 @@ is set to `true` if `server.ssl.certificate` and `server.ssl.key` are set. Set
|
|||
this to `true` if SSL is configured outside of {kib} (for example, you are
|
||||
routing requests through a load balancer or proxy).
|
||||
|
||||
`xpack.security.sameSiteCookies`::
|
||||
Sets the `SameSite` attribute of the session cookie. This allows you to declare whether your cookie should be restricted to a first-party or same-site context.
|
||||
Valid values are `Strict`, `Lax`, `None`.
|
||||
This is *not set* by default, which modern browsers will treat as `Lax`. If you use {kib} embedded in an iframe in modern browsers, you might need to set it to `None`. Setting this value to `None` requires cookies to be sent over a secure connection by setting `xpack.security.secureCookies: true`. Some old versions of IE11 do not support `SameSite: None`.
|
||||
|
||||
`xpack.security.sessionTimeout`::
|
||||
Sets the session duration (in milliseconds). By default, sessions stay active
|
||||
until the browser is closed. When this is set to an explicit timeout, closing the
|
||||
|
|
|
@ -38,6 +38,7 @@ export const security = (kibana) => new kibana.Plugin({
|
|||
encryptionKey: Joi.string(),
|
||||
sessionTimeout: Joi.number().allow(null).default(null),
|
||||
secureCookies: Joi.boolean().default(false),
|
||||
sameSiteCookies: Joi.string().valid(['Strict', 'Lax', 'None']).optional(),
|
||||
loginAssistanceMessage: Joi.string(),
|
||||
public: Joi.object({
|
||||
protocol: Joi.string().valid(['http', 'https']),
|
||||
|
|
|
@ -50,6 +50,58 @@ describe('Session', () => {
|
|||
path: 'base/path/'
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an exception if "SameSite: None" set on not Secure connection', async () => {
|
||||
config.get.withArgs('xpack.security.secureCookies').returns(false);
|
||||
config.get.withArgs('xpack.security.sameSiteCookies').returns('None');
|
||||
|
||||
try {
|
||||
await Session.create(server);
|
||||
expect().fail('`Session.create` should fail.');
|
||||
} catch(err) {
|
||||
expect(err).to.be.a(Error);
|
||||
expect(err.message).to.be('"SameSite: None" requires Secure connection');
|
||||
}
|
||||
});
|
||||
|
||||
it('sets isSameSite:false when sameSiteCookies: None', async () => {
|
||||
config.get.withArgs('xpack.security.cookieName').returns('cookie-name');
|
||||
config.get.withArgs('xpack.security.encryptionKey').returns('encryption-key');
|
||||
config.get.withArgs('server.basePath').returns('base/path');
|
||||
config.get.withArgs('xpack.security.secureCookies').returns(true);
|
||||
config.get.withArgs('xpack.security.sameSiteCookies').returns('None');
|
||||
|
||||
await Session.create(server);
|
||||
|
||||
sinon.assert.calledOnce(server.auth.strategy);
|
||||
sinon.assert.calledWith(server.auth.strategy, 'security-cookie', 'cookie', sinon.match.has('isSameSite', false));
|
||||
});
|
||||
|
||||
it('sets isSameSite:Lax when sameSiteCookies: Lax', async () => {
|
||||
config.get.withArgs('xpack.security.cookieName').returns('cookie-name');
|
||||
config.get.withArgs('xpack.security.encryptionKey').returns('encryption-key');
|
||||
config.get.withArgs('server.basePath').returns('base/path');
|
||||
config.get.withArgs('xpack.security.secureCookies').returns(true);
|
||||
config.get.withArgs('xpack.security.sameSiteCookies').returns('Lax');
|
||||
|
||||
await Session.create(server);
|
||||
|
||||
sinon.assert.calledOnce(server.auth.strategy);
|
||||
sinon.assert.calledWith(server.auth.strategy, 'security-cookie', 'cookie', sinon.match.has('isSameSite', 'Lax'));
|
||||
});
|
||||
|
||||
it('sets isSameSite:Strict when sameSiteCookies: Strict', async () => {
|
||||
config.get.withArgs('xpack.security.cookieName').returns('cookie-name');
|
||||
config.get.withArgs('xpack.security.encryptionKey').returns('encryption-key');
|
||||
config.get.withArgs('server.basePath').returns('base/path');
|
||||
config.get.withArgs('xpack.security.secureCookies').returns(true);
|
||||
config.get.withArgs('xpack.security.sameSiteCookies').returns('Strict');
|
||||
|
||||
await Session.create(server);
|
||||
|
||||
sinon.assert.calledOnce(server.auth.strategy);
|
||||
sinon.assert.calledWith(server.auth.strategy, 'security-cookie', 'cookie', sinon.match.has('isSameSite', 'Strict'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('`get` method', () => {
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
*/
|
||||
|
||||
import hapiAuthCookie from 'hapi-auth-cookie';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import Statehood from 'statehood';
|
||||
|
||||
const HAPI_STRATEGY_NAME = 'security-cookie';
|
||||
// Forbid applying of Hapi authentication strategies to routes automatically.
|
||||
|
@ -121,6 +123,11 @@ export class Session {
|
|||
const password = config.get('xpack.security.encryptionKey');
|
||||
const path = `${config.get('server.basePath')}/`;
|
||||
const secure = config.get('xpack.security.secureCookies');
|
||||
const sameSiteCookies = config.get('xpack.security.sameSiteCookies');
|
||||
|
||||
if (sameSiteCookies === 'None' && secure !== true) {
|
||||
throw new Error('"SameSite: None" requires Secure connection');
|
||||
}
|
||||
|
||||
server.auth.strategy(HAPI_STRATEGY_NAME, 'cookie', {
|
||||
cookie: name,
|
||||
|
@ -129,10 +136,23 @@ export class Session {
|
|||
validateFunc: Session._validateCookie,
|
||||
isHttpOnly: httpOnly,
|
||||
isSecure: secure,
|
||||
isSameSite: false,
|
||||
isSameSite: sameSiteCookies === 'None' ? false : sameSiteCookies || false,
|
||||
path: path,
|
||||
});
|
||||
|
||||
// A hack to support SameSite: 'None'.
|
||||
// Remove it after update Hapi to v19 that supports SameSite: 'None' out of the box.
|
||||
if (sameSiteCookies === 'None') {
|
||||
server.log(['debug', 'security', 'auth', 'session'], 'Patching Statehood.prepareValue');
|
||||
const originalPrepareValue = Statehood.prepareValue;
|
||||
Statehood.prepareValue = function kibanaStatehoodPrepareValueWrapper(key, value, options) {
|
||||
if (key === name) {
|
||||
options.isSameSite = 'None';
|
||||
}
|
||||
return originalPrepareValue(key, value, options);
|
||||
};
|
||||
}
|
||||
|
||||
if (HAPI_STRATEGY_MODE) {
|
||||
server.auth.default({
|
||||
strategy: HAPI_STRATEGY_NAME,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue