Skip to content
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

Merged
merged 7 commits into from
Nov 27, 2019

Conversation

jportner
Copy link
Contributor

@jportner jportner commented Nov 13, 2019

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:

  • Add path and secure attributes to encrypted cookie session value contents
  • Modify session cookie validation check to detect and invalidate cookies with outdated attributes that do not match the current server configuration

Note: 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 strikethroughs to remove checklist items you don't feel are applicable to this PR.

For maintainers

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 });
Copy link
Contributor Author

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,
Copy link
Contributor Author

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.

Copy link
Contributor Author

@jportner jportner left a 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.

@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💔 Build Failed

Copy link
Contributor

@kobelb kobelb left a 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?

x-pack/plugins/security/server/authentication/index.ts Outdated Show resolved Hide resolved
Cookies are now checked for attributes that match the current
Kibana configuration.
@jportner
Copy link
Contributor Author

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.

Ah, I tested with https enabled and toggling xpack.security.secureCookies between true and false (which may only happen if Kibana is sitting behind a reverse proxy). The current code does catch that difference and invalidate the cookie. However, we don't need to specify whether the cookie is Secure or not, because that attribute doesn't determine the scope of the cookie -- only Path and Domain determine scope. So the cookie would get overwritten anyway whenever the session is updated.

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/
Apparently Google has proposed the "Strict Secure Cookies" specification, which is causing the issue you are describing. In this spec, a browser with an insecure connection (e.g., http) cannot overwrite or delete a Secure cookie.

It looks like this change was accepted in RFC 6265 version 01 (April 2017), and this shipped/enabled in Chrome 58 and Firefox 52.

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?

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.
@jportner jportner marked this pull request as ready for review November 14, 2019 15:37
@jportner jportner requested review from a team as code owners November 14, 2019 15:37
@jportner jportner requested a review from kobelb November 14, 2019 15:38
@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@jportner jportner added the Team:Security Team focused on: Auth, Users, Roles, Spaces, Audit Logging, and more! label Nov 15, 2019
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-security (Team:Security)

Comment on lines 106 to 137
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 };
},
Copy link
Contributor

@pgayvallet pgayvallet Nov 16, 2019

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.

Copy link
Contributor Author

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:

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

Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor

@mshustov mshustov Nov 25, 2019

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

Copy link
Contributor Author

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?

@jportner jportner changed the title Add extra validation checks for cookies Fix infinite redirect loop when multiple cookies are sent Nov 21, 2019
const array: ProviderSession[] = Array.isArray(session) ? session : [session];
for (const sess of array) {
if (!isValid(sess)) {
return { isValid: false, path: sess.path };
Copy link
Contributor

@mshustov mshustov Nov 22, 2019

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?

Copy link
Contributor Author

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.
@elasticmachine
Copy link
Contributor

💔 Build Failed

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

@elasticmachine
Copy link
Contributor

💚 Build Succeeded

Copy link
Contributor

@pgayvallet pgayvallet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

platform changes LGTM

@jportner jportner merged commit 7830946 into elastic:master Nov 27, 2019
@jportner jportner deleted the issue-36825-multiple-cookies branch November 27, 2019 14:43
jportner added a commit to jportner/kibana that referenced this pull request Nov 27, 2019
)

Cookies are now checked for attributes that match the current
Kibana configuration. Invalid cookies are cleared more reliably.
jportner added a commit that referenced this pull request Nov 27, 2019
…51821)

Cookies are now checked for attributes that match the current
Kibana configuration. Invalid cookies are cleared more reliably.
jloleysens added a commit to jloleysens/kibana that referenced this pull request Nov 27, 2019
* 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)
mbondyra added a commit to mbondyra/kibana that referenced this pull request Nov 28, 2019
…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)
  ...
jloleysens added a commit to jloleysens/kibana that referenced this pull request Nov 28, 2019
…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)
  ...
timductive pushed a commit to timductive/kibana that referenced this pull request Dec 16, 2019
)

Cookies are now checked for attributes that match the current
Kibana configuration. Invalid cookies are cleared more reliably.
@azasypkin azasypkin mentioned this pull request Aug 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release_note:fix Team:Security Team focused on: Auth, Users, Roles, Spaces, Audit Logging, and more! v7.6.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Multiple cookies and infinite redirects
5 participants