-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix infinite redirect loop when multiple cookies are sent #50452
Fix infinite redirect loop when multiple cookies are sent #50452
Conversation
log.debug(`Clearing invalid session cookie`); | ||
// need to use Hapi toolkit to clear cookie with defined options | ||
if (req) { | ||
(req.cookieAuth as any).h.unstate(cookieOptions.name, { path, isSecure }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needed to add code to clear invalid cookies by specifying the path
and isSecure
attributes (using the Hapi toolkit).
Note, because of this, we no longer need Hapi to automatically clear invalid cookies (see below).
isSecure: cookieOptions.isSecure, | ||
path: basePath, | ||
clearInvalid: true, | ||
clearInvalid: false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above, we no longer need Hapi to automatically clear invalid cookies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Author's notes for other reviewers.
💔 Build Failed
|
💚 Build Succeeded
|
70fcfe0
to
f74e598
Compare
💔 Build Failed
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fixes the issue when going from being hosted at a non-basepath to being hosted at a basepath. However, it doesn't address the issue when going from https to http. Others have run into the https to http issue, and https://www.petefreitag.com/item/857.cfm describes the same situation.
Let's address the https to http issue in a separate PR. I think using a different cookie name for https vs http would be a good solution here, but it's something we'd want to do in a major version upgrade. In light of this information, do you mind removing the "secure cookies" part of this PR, since it's not fixing the issue?
Cookies are now checked for attributes that match the current Kibana configuration.
Ah, I tested with https enabled and toggling However, I didn't test like you mentioned above, going from https to http. I did a bit of reading and found this article: https://www.sjoerdlangkemper.nl/2017/10/25/strict-secure-cookies/ It looks like this change was accepted in RFC 6265 version 01 (April 2017), and this shipped/enabled in Chrome 58 and Firefox 52.
Agree, and we should look at using Cookie Prefixes when that time comes. |
We don't need to check this attribute, as it doesn't influence the scope of the cookie. Only `path` infleunces the cookie's scope.
f74e598
to
60d7789
Compare
💚 Build Succeeded |
Pinging @elastic/kibana-security (Team:Security) |
validateFunc: async (req, session: T) => ({ valid: await cookieOptions.validate(session) }), | ||
validateFunc: async (req, session: T) => { | ||
const result = cookieOptions.validate(session); | ||
if (!result.isValid) { | ||
clearInvalidCookie(req, result.path); | ||
} | ||
return { valid: result.isValid }; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to keep the functionality while calling clearInvalidCookie
outside of validateFunc
? Adding behaviour in a validation method looks like introducing side effects to me, so I would prefer to avoid it if we can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love that it's there, but I couldn't think of a better place to put it.
I originally had it in the X-Pack authentication module in the Security plugin:
kibana/x-pack/plugins/security/server/authentication/index.ts
Lines 68 to 99 in 60d7789
const isValid = (sessionValue: ProviderSession) => { | |
// ensure that this cookie was created with the current Kibana configuration | |
const { path, expires } = sessionValue; | |
if (path !== undefined && path !== (http.basePath.serverBasePath || '/')) { | |
authLogger.debug(`Outdated session value with path "${sessionValue.path}"`); | |
return false; | |
} | |
// ensure that this cookie is not expired | |
return !(expires && expires < Date.now()); | |
}; | |
const authenticator = new Authenticator({ | |
clusterClient, | |
basePath: http.basePath, | |
config: { sessionTimeout: config.sessionTimeout, authc: config.authc }, | |
isSystemAPIRequest: (request: KibanaRequest) => getLegacyAPI().isSystemAPIRequest(request), | |
loggers, | |
sessionStorageFactory: await http.createCookieSessionStorageFactory({ | |
encryptionKey: config.encryptionKey, | |
isSecure: config.secureCookies, | |
name: config.cookieName, | |
validate: (session: ProviderSession) => { | |
const array: ProviderSession[] = Array.isArray(session) ? session : [session]; | |
for (const sess of array) { | |
if (!isValid(sess)) { | |
return { isValid: false, path: sess.path }; | |
} | |
} | |
return { isValid: true }; | |
}, | |
}), | |
}); |
However, I wanted to keep usage of the Hapi toolkit abstracted away from X-Pack, so I decided to move that code into Core in
cookie_session_storage.ts
.
If you have a better idea of where to put it, I'm all ears!
FWIW, Hapi's default behavior to clear the cookie (which I've overridden in this commit) already originates in its validation method. https://github.com/hapijs/cookie/blob/3a6e046e7adafafc382054000239c338a1a55d8c/lib/index.js#L197-L199
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it's we are doing nothing that hapi was already doing, this should be alright. Unless someone has a better idea on where we could move this (don't have much experience on hapi lifecycle), maybe @joshdover or @restrry?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the platform doesn't handle the removal, but the plugin controls it? Security plugin already has access to CookieStorage and can perform all setup/teardown logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about that, but we are relying on the Hapi toolkit for the actual cookie removal -- right now we don't expose Hapi directly to plugins in the New Platform, we abstract it away. My impression was that we wanted to keep it that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, but we can extend API and provide methods to remove the cookie. This method will be a wrapper around hapi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, nvm. The core defines the auth strategy and sets the cookie in hapi. Probably, it's better to keep your current implementation with explicit removal in the core. Ok for me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, thanks!
@pgayvallet any other concerns or nits?
x-pack/plugins/security/server/authentication/authenticator.test.ts
Outdated
Show resolved
Hide resolved
const array: ProviderSession[] = Array.isArray(session) ? session : [session]; | ||
for (const sess of array) { | ||
if (!isValid(sess)) { | ||
return { isValid: false, path: sess.path }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, validate
function returns on the very first invalid cookie and removes only it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's correct.
It's because we're using the Hapi toolkit to clear the cookie (which is the same method that the Hapi Cookie plugin uses under the hood in our current implementation -- we are just passing it an extra parameter). However, the Hapi toolkit can only clear one cookie at a time. This sounded like an acceptable approach to me because:
- The only time we'll ever see multiple cookies is if the client has stored several more than one cookie with the same name and different, overlapping paths (e.g.,
/
and/foo
) - If multiple cookies are sent and they are all invalid (very unlikely), the client will make multiple round trips and delete each one; this will happen near-instantaneously and be imperceptible to the end user
The alternative was to try to reimplement that logic in a different way so we could delete multiple cookies in a single response, but that seemed unnecessary and risky -- we know the existing method works, and we want to avoid mucking around with Hapi under the hood as much as possible.
The validate callback can expect to receive ProviderSession or an array of ProviderSessions. The types have been updated to reflect that.
💔 Build Failed |
💚 Build Succeeded |
💚 Build Succeeded |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
platform changes LGTM
* upstream/7.x: Fix infinite redirect loop when multiple cookies are sent (elastic#50452) (elastic#51821) [Console] Proxy fallback (elastic#50185) (elastic#51814) Added endgame-* index and new heading 3 Elastic Endpoint SMP. (elastic#51071) (elastic#51828) [Maps] Added options to disable zoom, hide tool tips, widgets/overlays in embeddable maps (elastic#50663) (elastic#51811) Move errors and validate index pattern ⇒ NP (elastic#51805) (elastic#51831) [SIEM][Detection Engine] Adds ecs threat properties to rules (elastic#51782) (elastic#51827) [Lens] Remove client-side reference to server source code (elastic#51763) (elastic#51825) fixes drag and drop in tests (elastic#51806) (elastic#51813) [Uptime] Redesign/44541 new monitor list expanded row (elastic#46567) (elastic#51809) [7.x] [Telemetry] collector set to np (elastic#51618) (elastic#51787) [Uptime] added test for chart wrapper (elastic#50399) (elastic#51808) Expressions service fixes: better error and loading states handling (elastic#51183) (elastic#51800) Query String(Bar) Input - cleanup (elastic#51598) (elastic#51804) [ML] Adjust and re-enable categorization advanced wizard test (elastic#51005) (elastic#51017) fixes url state tests (elastic#51746) (elastic#51798) fixes browser field tests (elastic#51738) (elastic#51799) [Task Manager] Tests for the ability to run tasks of varying durations in parallel (elastic#51572) (elastic#51701) [ML] Fix anomaly detection test suite (elastic#51712) (elastic#51795) [SIEM] Fix Timeline drag and drop behavior (elastic#51558) (elastic#51793)
…ra/kibana into IS-46410_remove-@kbn/ui-framework * 'IS-46410_remove-@kbn/ui-framework' of github.com:mbondyra/kibana: (49 commits) [ML] Re-activate after method in transform test (elastic#51815) [SIEM] [Detection Engine] Add edit on rule creation (elastic#51670) De-angularize visLegend (elastic#50613) [SIEM][Detection Engine] Change security model to use SIEM permissions [Monitoring] Sass cleanup (elastic#51100) Move errors and validate index pattern ⇒ NP (elastic#51805) fixes pagination tests (elastic#51822) Split legacy plugin discovery, expose SavedObjects scopedClient, wrappers, repository (elastic#48882) [SIEM][Detection Engine] Adds ecs threat properties to rules (elastic#51782) [Lens] Remove client-side reference to server source code (elastic#51763) Fix infinite redirect loop when multiple cookies are sent (elastic#50452) fixes drag and drop in tests (elastic#51806) [Console] Proxy fallback (elastic#50185) Query String(Bar) Input - cleanup (elastic#51598) shim visualizations plugin (elastic#50624) Expressions service fixes: better error and loading states handling (elastic#51183) fixes url state tests (elastic#51746) fixes browser field tests (elastic#51738) [ML] Fix anomaly detection test suite (elastic#51712) [SIEM] Fix Timeline drag and drop behavior (elastic#51558) ...
…license-management * 'master' of github.com:elastic/kibana: (48 commits) Enable alerting and actions plugin by default (elastic#51254) Fix error returned when creating an alert with ES security disabled (elastic#51639) [Discover] Improve Percy functional tests (elastic#51699) fixes timeline data providers tests (elastic#51862) [Dependencies]: upgrade react to latest v16.12.0 (elastic#51145) Allow routes to define some payload config values (elastic#50783) Move saved queries service + language switcher ⇒ NP (elastic#51812) [ML] Re-activate after method in transform test (elastic#51815) [SIEM] [Detection Engine] Add edit on rule creation (elastic#51670) De-angularize visLegend (elastic#50613) [SIEM][Detection Engine] Change security model to use SIEM permissions [Monitoring] Sass cleanup (elastic#51100) Move errors and validate index pattern ⇒ NP (elastic#51805) fixes pagination tests (elastic#51822) Split legacy plugin discovery, expose SavedObjects scopedClient, wrappers, repository (elastic#48882) [SIEM][Detection Engine] Adds ecs threat properties to rules (elastic#51782) [Lens] Remove client-side reference to server source code (elastic#51763) Fix infinite redirect loop when multiple cookies are sent (elastic#50452) fixes drag and drop in tests (elastic#51806) [Console] Proxy fallback (elastic#50185) ...
Summary
The existing session cookie implementation was brittle in terms of handling changes to configuration. Specifically, if a Kibana installation change its base path, or enabled secure session cookies, any existing cookies would still be sent by the browser. However, these outdated cookies would not ever be deleted because of the default behavior of the Hapi cookie authentication plugin.
I made the following changes:
path
andsecure
attributes to encrypted cookie session value contentsNote: cookies that do not contain these attributes are assumed to have been created with the current configuration. I.e., this will only resolve configuration mismatch issues for cookies that were created with this change in place.
Resolves #36825.
Checklist
Use
strikethroughsto remove checklist items you don't feel are applicable to this PR.[ ] Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support[ ] Documentation was added for features that require explanation or tutorials[ ] This was checked for keyboard-only and screenreader accessibilityFor maintainers
[ ] This includes a feature addition or change that requires a release note and was labeled appropriately