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
SEC-3108: DigestAuthenticationFilter should use SecurityContextHolder.createEmptyContext() #3310
Comments
Rob Winch said: Can you expand on how
As far as I can tell this would only happen if the provided I'm also interested in why and how |
Kristian Laakkonen said:
The Authentication object is constructed from a UserDetails object in DigestAuthenticationFilter.java, line 233. If createAuthenticatedToken is false, the authorities are not copied from UserDetails, and the Authentication object is created without authorities.
AbstractSecurityInterceptor calls AuthenticationManager.authenticate() in AbstractSecurityInterceptor.java, line 353 and finally we end up in AbstractUserDetailsAuthenticationProvider.java, line 125 where a new Authentication token is created with the authorities set. I don't know why the full authentication process is not carried out in DigestAuthenticationFilter and AuthenticationManager is not used there and instead UserDetailsService is accessed directly. Actually Spring Security Reference says that UserDetailsService is needed because ??DigestAuthenticationFilter must have direct access to the clear text password of a user??. |
Rob Winch said: Thank you for the explanation. Can you set createAuthenticatedToken to true? |
Kristian Laakkonen said: Setting createAuthenticatedToken to true would mean that disabled users could log in, which is not acceptable. According to documentation and code it seems that the isEnabled() flag of UserDetails is not checked in that case (I have not actually tested it). |
Rob Winch said: So getting the the heart of the problem...it sounds as though you need to be able to inject a UserDetailsChecker to verify the state of the user details. Does that sound right? One workaround is to perform this verification in the UserDetailsService itself. This would ensure that you were able to get past your issue until we can get a release out. |
Kristian Laakkonen said: Being able to inject a UserDetailsChecker and then setting createAuthenticatedToken to true would probably solve the problem in my case, yes. But the original concurrency problem is still there... I think there should be at least some documentation then that createAuthenticatedToken must be true if there can be concurrent requests. |
Rob Winch said: Honestly, I'm still a little confused on the concurrency issue. I understand that in request 1 the Without the response being committed the client will not be able to access the So my question is, "What is committing the |
Kristian Laakkonen said: In my example the user has already been authenticated and the session has been created before requests 1 and 2 so SecurityContext and JSESSIONID are already available. Browsers may send the Digest Authorization header even after the user has already been authenticated. This causes the DigestAuthenticationFilter to do the authentication procedure again and if there are two simultaneous requests that send the header, the problem appears. |
Rob Winch said: Thanks for the explanation. The reason this is causing an issue is due to the fact that: SecurityContextHolder.getContext().setAuthentication(...); is being used. This would not be an issue when a new session is created. However, since the session already exists it is updating the same SecurityContext instance that the other sessions are referring to. If we use SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(...);
SecurityContextHolder.setContext(context); we would no longer be updating the SecurityContext other threads are referring to. This would mean the SecurityContext with no authorities would not be available until the response was committed (after the roles were then populated). |
Kristian Laakkonen said: Seems good to me. Thanks for taking time to investigate this. |
Rob Winch said: Thanks for the report. This is now fixed in 3.2.x, 4.0.x, and master |
Kristian Laakkonen (Migrated from SEC-3108) said:
When using Digest authentication and there are concurrent requests from the same session, it is possible that the Authentication object returned by SessionContextHolder.getContext().getAuthentication() does not have its authorities set. Then, e.g. HttpServletRequest.isUserInRole() returns sometimes incorrectly false even if the user actually has that role.
This happens because with Digest authentication, constructing the Authentication object is done in two steps, but the incomplete Authentication object is made visible to another threads after first step.
Consider the following scenario:
I could set createAuthenticatedToken = true in DigestAuthenticationFilter, but the comment on the set method says that user flags like isEnabled() or isAccountNonExpired() are not checked in that case, so this is not a solution.
The text was updated successfully, but these errors were encountered: