Skip to content

[Security] Deprecated not being logged out after user change #23882

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

Closed
wants to merge 3 commits into from
Closed

[Security] Deprecated not being logged out after user change #23882

wants to merge 3 commits into from

Conversation

linaori
Copy link
Contributor

@linaori linaori commented Aug 14, 2017

Q A
Branch? 3.4
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? yes
Tests pass? yes
Fixed tickets #17023
License MIT
Doc PR ~

This PR is an alternative approach to #19033. Due to a behavioral change that could break a lot of applications and websites, I've decided to trigger a deprecation instead of actually changing the behavior as that can be done for 4.0.

Whenever a user object is considered changed (AbstractToken::hasUserChanged) when setting a new user object after refreshing, it will now throw a deprecation, paving the way for a behavioral change in 4.0. The idea is that in 4.0 Symfony will simply trigger a logout when this case is encountered.

@linaori
Copy link
Contributor Author

linaori commented Aug 14, 2017

Test failure not related to this PR it seems

@nicolas-grekas nicolas-grekas added this to the 3.4 milestone Aug 14, 2017
@weaverryan
Copy link
Member

Woohoo! I'll try to review this tonight. Simple patch... but I'd like to try it in a few situations to check for side effects.

About deprecating this in 3.4 and actually changing it in 4.0, I think I agree. It's a potentially impactful change... and a non-obvious one (if you upgraded to 3.4 and the new code logged you out, you might not notice this at first - as it may only affect an edge-case situation). Due to the fact that this may only happen in some situations, the deprecation also isn't a perfect notifier, but it'll give people a better chance.

@weaverryan
Copy link
Member

Actually, on second thought, the deprecation is not quite right: there is no action the developer could take on 3.4 to stop hitting the deprecation. Instead, it's just a warning that says "hey! This code path will change in the future". Then, on 4.0, they will have different results than 3.4. That's a problem, since it's not how we intend for deprecations to work.

@iltar wdyt about this?

@linaori
Copy link
Contributor Author

linaori commented Aug 16, 2017

@weaverryan it's a default behavior change that can break a lot of things, so I feel a deprecation is in place. However, you're right about this being kind of passive. What about a temporary parameter that you can turn on to trigger an exception here? Could be done in a similar fashion to several changes in 2.8 and 3.0, like swapping the choice keys/values, where the option gets removed for 5.0.

I'm not entirely sure on how to pass a message to the front-end though.

$this->logger->debug('Token was deauthenticated after trying to refresh it.', array('username' => $refreshedUser->getUsername(), 'provider' => get_class($provider)));
}

@trigger_error('Refreshing a deauthenticated user is deprecated as of 3.4 and will trigger a logout in 4.0.', E_USER_DEPRECATED);
Copy link
Member

Choose a reason for hiding this comment

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

a changelog+upgrade entries would be great

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 will add them as soon as I know how to proceed with the actual logging out users, as this might need more additions to the changelog and upgrade entries 👍

@weaverryan
Copy link
Member

Yea, it seems to me, that we indeed do need to do something similar to the choice keys/values thing. Perhaps someone else has an idea but I think it's a good start. So, we could add the following config:

security:
    firewall:
        main:
            logout_on_user_change: true

If this new config key is NOT set, then we'll trigger the deprecation warning. This would require everyone to set this value explicitly (to true or false)... which is kind of annoying, but super explicit. In 4.0, this option would still exist, but would throw an exception if not set. Then, if we wanted, in 5.0, it could default to true (so that you don't need to explicitly configure it). The ContextListener would use this to know if it should log the user out or not.

This is the most conservative option I can think of: it absolutely doesn't break BC... but it's also kind of annoying. If you can't think of a better idea, let's do this first - it's easier to get a +1 or -1 from people if we're looking at real code.

@chalasr
Copy link
Member

chalasr commented Sep 3, 2017

Such key should not be mandatory forstateless firewalls.

@chalasr
Copy link
Member

chalasr commented Sep 20, 2017

@iltar Would you have time to finish this? I can try to help otherwise, feature freeze is coming.

@linaori
Copy link
Contributor Author

linaori commented Sep 21, 2017

@chalasr I'll put it on my todo list for upcoming Friday

@linaori
Copy link
Contributor Author

linaori commented Sep 21, 2017

I've updated the code to actually return null in the case where the context is set to true. I have not yet added this config to the firewall, I first wanted to know if this is okay. It doesn't trigger a message for the user after it was unset though, not sure if that's possible to add in this scenario.

@chalasr chalasr self-requested a review September 21, 2017 15:56
@linaori
Copy link
Contributor Author

linaori commented Sep 22, 2017

Not sure if the test failures are something I can fix, seems to be an issue because it pulls in a different version than what my PR provides

@ThePeterMick
Copy link

@iltar - thanks a lot for looking into this! Very much appreciated.

In the meantime should we update the page at https://symfony.com/doc/current/security/entity_provider.html#understanding-serialize-and-how-a-user-is-saved-in-the-session, especially the line of "For example, if the username on the 2 User objects doesn't match for some reason, then the user will be logged out for security reasons".

At the moment it sounds like the appropriate description there should be "it checks the properties to determine if the roles should be reloaded"

Just an example right here how people follow this guidance and accept answers (hope they test all that, to find out it is not working): https://stackoverflow.com/questions/42877881/symfony-check-if-users-email-changed-while-logged-in

Also, when you currently change behind the scenes what the isEnabled() function returns between requests, it will log the user out (it is just other properties for which it does not log out the user), do you know where this behvaiour is triggered?

Finally, is there any workaround at the moment for this - to logout the user if any serialized properties change?

Cheers!

@linaori
Copy link
Contributor Author

linaori commented Sep 22, 2017

Finally, is there any workaround at the moment for this - to logout the user if any serialized properties change?

I guess you can reproduce a similar result by overriding some of the pre-defined classes and change the behavior in the authenticators, not 100% sure on that as it's quite a complicated change.

UPGRADE-3.4.md Outdated
@@ -269,6 +269,11 @@ SecurityBundle
as first argument. Not passing it is deprecated and will throw a `TypeError`
in 4.0.

* Added `logout_on_user_change` to the firewall options. This config item will
trigger a logout when the user has changed. should be set to true to avoid
Copy link
Contributor

Choose a reason for hiding this comment

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

should -> Should

@@ -13,6 +13,10 @@ CHANGELOG
* `SetAclCommand::__construct()` now takes an instance of
`Symfony\Component\Security\Acl\Model\MutableAclProviderInterfaceConnection`
as first argument
* Added `logout_on_user_change` to the firewall options. This config item will
trigger a logout when the user has changed. should be set to true to avoid
Copy link
Contributor

Choose a reason for hiding this comment

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

should -> Should

@linaori
Copy link
Contributor Author

linaori commented Sep 25, 2017

status: Needs Review

@@ -198,7 +220,7 @@ protected function refreshUser(TokenInterface $token)
}

if ($userNotFoundByProvider) {
return;
return null;
Copy link
Contributor

@ogizanagi ogizanagi Sep 25, 2017

Choose a reason for hiding this comment

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

To be reverted as it should be applied to the 2.7 branch. But it is worth a dedicated PR on the whole codebase. Related to #17201.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will fix

@linaori
Copy link
Contributor Author

linaori commented Sep 26, 2017

In that case, I've added a note here. It feels a bit weird having a non-deprecated method that does nothing in 4.0, but it's even weirder to have a fresh installation of 4.0 cause a deprecation.

@eXtreme
Copy link
Contributor

eXtreme commented Sep 26, 2017

Great to see that finally being taken care of 👍

@@ -61,14 +61,23 @@ public function __construct(TokenStorageInterface $tokenStorage, $userProviders,

$this->tokenStorage = $tokenStorage;
$this->userProviders = $userProviders;
$this->contextKey = $contextKey;
Copy link
Member

Choose a reason for hiding this comment

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

I think we need to deprecate the constructor argument if the class doesn't need it anymore. That can be done in another PR, could you revert this change (with the property removal)?

Copy link
Contributor

@ogizanagi ogizanagi Sep 26, 2017

Choose a reason for hiding this comment

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

It's still used for the sessionKey below. Only the property is removed because it's not used. (However again, it could have been done on the 2.7 branch ^^)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct, I've merely removed the property because the property wasn't used anymore. If desired, I can change this in a lower branch instead.

Copy link
Member

Choose a reason for hiding this comment

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

@iltar that would be nice

Copy link
Contributor Author

Choose a reason for hiding this comment

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

see #24329

nicolas-grekas added a commit that referenced this pull request Sep 26, 2017
…tar)

This PR was merged into the 2.7 branch.

Discussion
----------

Added null as explicit return type (?TokenInterface)

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #23882 (comment)
| License       | MIT
| Doc PR        | ~

This fixes the returntype in the `ContextListener` so it can be merged upwards.

/cc @chalasr

Commits
-------

1ba4dd9 Added null as explicit return type (?TokenInterface)
nicolas-grekas added a commit that referenced this pull request Sep 26, 2017
…iltar)

This PR was merged into the 2.7 branch.

Discussion
----------

Removed unused private property in the ContextListener

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #23882 (comment)
| License       | MIT
| Doc PR        | ~

The context property is unused, the local variable (constructor argument) is only concatenated to a string inside (session key).

Commits
-------

6522c05 Removed unused private property
@chalasr
Copy link
Member

chalasr commented Sep 26, 2017

Thank you @iltar.

chalasr pushed a commit that referenced this pull request Sep 26, 2017
…change (iltar)

This PR was squashed before being merged into the 3.4 branch (closes #23882).

Discussion
----------

[Security] Deprecated not being logged out after user change

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #17023
| License       | MIT
| Doc PR        | ~

This PR is an alternative approach to #19033. Due to a behavioral change that could break a lot of applications and websites, I've decided to trigger a deprecation instead of actually changing the behavior as that can be done for 4.0.

Whenever a user object is considered changed (`AbstractToken::hasUserChanged`) when setting a new user object after refreshing, it will now throw a deprecation, paving the way for a behavioral change in 4.0. The idea is that in 4.0 Symfony will simply trigger a logout when this case is encountered.

Commits
-------

22f525b [Security] Deprecated not being logged out after user change
@chalasr chalasr closed this Sep 26, 2017
@weaverryan
Copy link
Member

Woohoo! Thank you @iltar!

@ThePeterMick
Copy link

Thank you very much @iltar!

@kbond
Copy link
Member

kbond commented Oct 11, 2017

This works as expected for the session but when remember_me is used, the user remains logged in (and authenticated). How can I address this?

@chalasr
Copy link
Member

chalasr commented Oct 11, 2017

@kbond would you mind opening an issue so we can keep track of this bug and discuss the possible solutions if needed?

@kbond
Copy link
Member

kbond commented Oct 11, 2017

Ok, opened issue #24525

@eXtreme
Copy link
Contributor

eXtreme commented Oct 11, 2017

oh, remember_me, hmm I wonder whether Guard is also affected by this? And if not, how?

@kbond
Copy link
Member

kbond commented Oct 11, 2017

@eXtreme guard is affected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants