-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[WIP] [Security] Fix #2172 User switching is not available for pre-authenticated users (oldest open bug!) #19059
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
Conversation
…token, and so allow switching users
$token = $this->tokenStorage->getToken(); | ||
if(null !== $token && false === ($token instanceof PreAuthenticatedToken) && $token->isAuthenticated()) { | ||
if (null !== $this->logger) { | ||
$this->logger->debug('An authenticated token is already provided, bypass PreAuthenticatedListener.', array('token' => (string) $this->tokenStorage->getToken())); |
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'd personally prefer to see the to string method being called here in case of refactoring one day
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.
You mean in the debug log?
I kept the same syntax than following logs.
But I realized I should use the $token variable in the log, don't need to recall the getter.
|
||
public static function setUpBeforeClass() | ||
{ | ||
parent::deleteTmpDir('StandardFormLogin'); |
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.
Why parent::
and not self::
or static::
?
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.
That's the way all the tests have already been wrotten, I just kept the same syntax.
In this context, it's the same, and maybe the more obvious to understand.
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.
If it's convention.. i guess it's okay then.
From my personal POV i think it's just weird... why bypass the current class layer?
Any feedback about this idea of bypassing the PreAuthenticatedListener check in case there is already an authenticated token provided in the context? |
@@ -54,6 +54,15 @@ public function __construct(TokenStorageInterface $tokenStorage, AuthenticationM | |||
*/ | |||
final public function handle(GetResponseEvent $event) | |||
{ | |||
$token = $this->tokenStorage->getToken(); | |||
if(null !== $token && !$token instanceof PreAuthenticatedToken && $token->isAuthenticated()) { |
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.
Missing space if (null !== ...
instead of if(null !==
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.
Oups, I missed this one. Thanks.
Done!
I don't know the Security component enough to have an opinion here. |
This PR breaks BC by changing the behavior of pre-authenticated listeners when they find an existing token. So adding this in 2.7 is just a no-go. and regarding your question about the change being correct, I don't think it is. It can introduce other bugs. Regarding the user-switching, the issue is that pre-authenticated listeners are meant to be used for stateless auth methods where the client sends the credentials again with each request and our user switching system works only for stateful authentication where the client authenticates once and then the auth is kept in the session (allowing to impersonate someone and have this info stored for subsequent requests). If your project uses a stateless auth method, an impersonation feature would probably have to be implemented in a stateless way too (where the client remembers the fact that it wants to impersonate someone instead of the server remembering it). so I'm voting 👎 here |
@stof any clue to help @pasdeloup fix the linked issue? Or do you consider it invalid? |
So it would mean the issue itself should be closed? It has been opened almost 5 years ago by developers using "non-stateless authentication with x509", so I suppose they mean they use a session so there must be a way to do something and the feature seems interesting. However, I personnaly don't need it (for now), I just took the oldest issue and tried to understand and fix it, so if you say the issue itself is irrelevant, I won't spend more time on it and will pick up something else. |
@stof @nicolas-grekas so what do we do? I close the PR and you close the corresponding issue or I try to work on a workaround without BC Break? |
@pasdeloup I would add a note in the docs saying that impersonification is not supported for pre-auth and close both this PR and the issue. |
Ok, I just submitted the PR #6673 for documentation. |
Thanks for your help @pasdeloup. This wasn't merged but it's been definitely helpful. Waitinf for your next PR now :-) |
It's 2017 year and looks like we still need this feature :) Let me describe our case why it's needed. We have a lot of internal SF2 services that provide API and UI for our internal users (not clients). All users use x509 certificates with eToken devices to securely access our system with given privileges. But sometime we experience a situation when there is an issue with our service and this issue is reproducing only for one single user. We can't just go and check it locally because we are international company with many offices across the world. To reproduce this issue we currently have two options: remote connect via terminal like TeamViewer/RAdmin (it can be painfully slow connection speed for example for Nigeria) or we need to request same rights for our developer on production server to reproduce an issue which is not good, because production environment contains sensitive information that should not be available for all of our developers. This is why I'm bumping this old PR. In my opinion, user switching could be a nice feature for stateless environment too, this can be either cookie (yes, for stateless authentication!) or a GET-parameter in the URL like ?_switch=Some.Username. Of course, this feature should be protected by separate privilege or role or even one-time tokens for developers. Without user switching we are doing dirty hack: in Best regards, Alexander |
TODO
For my first PR, I tried to fix the Symfony's oldest still open bug (2011!)
New tests have been added to reproduce the bug and are now passing.
To be reviewed carefully as it deals with security.
From my understanding, the problem comes from the AbstractPreAuthenticatedListener that is not stateless: whenever a token is passed, it has to preauthenticate again.
Problem, even if another type of token is passed (like a UsernamePasswordToken), it still tries to authenticate it and as it failed it creates a new one with the current user.
This behaviour doesn't work when switching user because the new artificial user token is automatically replaced by the current preauthenticated token.
So the patch bypass this check if (and only if) a token is already there (given by context), it is already correctly authenticated, and it is of another class than PreAuthenticatedToken (to limit risks of BC break).
Problem: this new behaviour breaks (by design) the following test:
Symfony\Component\Security\Http\Tests\Firewall\AbstractPreAuthenticatedListenerTest::testHandleWhenAuthenticationFailsWithDifferentToken
To be discussed: is this new behaviour correct (it may fix other bugs), or is there potential security issues? Should it be considered as a BC break?