SEC-2028: Support remember me and concurrency control #2253

Open
spring-issuemaster opened this Issue Aug 8, 2012 · 15 comments

Projects

None yet

1 participant

@spring-issuemaster

Rob Winch (Migrated from SEC-2028) said:

Currently the concurrency control does not work with remember me. It would be good to support this out of the box.

@spring-issuemaster

Vladimir Loshchin said:

This issue is very usefull for make a feature "How many users on the website".

@spring-issuemaster

Sergey Chunayev said:

Is this issue related to the fact that users authenticated with remember-me bypass sessionRegistry and I can't access their session when I disable it from an admin account? If so, I wonder why this has MINOR priority? This is very serious security hole, because I can't disable users in runtime, meaning that while disabled user works in the session created after remember-me authentication, this session is not available via sessionRegistry and I can't expire it.

@spring-issuemaster

Lucas Ward said:

Sergey. Yes, you are correct, remember-me doesn't mess with sessionRegistry at all. I agree that it's a security hole that many wouldn't see until it was too late and should at least be considered higher priority than minor. (But I am not an owner or even commiter of this project)

@spring-issuemaster

Lucas Ward said:

I should note though, that this bug doesn't allow someone that shouldn't be authenticated to get access. The basic premise of the remember me cookie is that someone can log in for whatever is the configured time (default is 14 days I believe) If you want to use both remember me AND have a mechanism to keep people locked out from an admin console, you'll need to add some application specific stuff to ensure they don't have access. Or don't use remember me.

@spring-issuemaster

Sergey Chunayev said:

Thanks, Lucas.
In my situation I have to use remember-me with significantly long expiration time. As I understand, the disabled user won't be able to log in only after he has been logged out, which clears remember-me cookie. After that, the login workflow detects that it is a disabled user and fails to authenticate.
What is the best approach for me to deal with the fact that I still have to be able to disable users from admin console and use remember-me? I think about creating a specific filter ordered after remember-me filter that checks the disabled status. This can be a bottleneck, because I'll have to query db on each request. However, we are using grails and GORM should cache these queries pretty well.

@spring-issuemaster

Lucas Ward said:

Can't you just remove the users roles? In my application you need at least something like ROLE_USER to do anything in the system. Removing that role would keep you from doing anything. So, I handle this scenario by removing all roles from the user. If you setup everything correctly, the user will go to some page telling them they aren't authorized to access it. You could probably do some custom logic to detect if they have no roles too.

@spring-issuemaster

Sergey Chunayev said:

I think this results in writing custom logic to check roles for each request, which in turn results in querying database to get the roles for each request, no? In this case a custom security filter would work best, but I was thinking about it as a last resort unless there are standard means in the spring security itself.
Or you are saying that if I just remove roles, spring will handle it accordingly and immediately, given that I properly use sec: tags?

@spring-issuemaster

Lucas Ward said:

Sergey,

What I have setup is that all of my controllers have @Secured(SOME_ROLE) on them. Spring Security already has a lot of mechanisms in place to check the 'roles' of a principal. And I believe you're right, it gets setup properly with correct usage of the security namespace. So, removing roles from a user will prevent them from using anything on my site, and the existing filters should pull back the roles when users are logged in.

@spring-issuemaster

Sergey Chunayev said:

Lucas,

Thanks for feedback.
I just tried to delete user roles from my user object but, unfortunately, I'm still hit by the same problem I started with - while user is authenticated with remember-me cookie, there's no way to invalidate it's session => RememberMeAuthenticationFilter.doFilter() invokes SecurityContextHolder.getContext().getAuthentication() which returns the authentication containing roles the user was logged in with initially. So, without hard logout, this approach doesn't work automatically - disabled user still remains enabled and with all roles.
Looks like the only way is a new filter that checks users placed right after remember-me auth filter.

@spring-issuemaster

Lucas Ward said:

I think I understand your issue a bit better now. Because the remember me cookie doesn't put SessionInformation into the SessionRegistry it's impossible to find that user's session and invalidate it like you would for a UsernamePassword authentication. I got halfway through a fix that extended the remember me classes to put a SessionInformation into the registry. I was able to invalidate sessions, but getting it to properly work with concurrent session controls was really difficult. I think righting your own filter would be easier and less error prone than trying to hack at the current remember me classes.

@spring-issuemaster

Sergey Chunayev said:

Lucas, thank you very much.
There's also hope that spring security team finds a way to address this issue in some standard way. I just wanted to make sure that the problem I'm having is a known issue and I'm not doing something wrong.

@spring-issuemaster

Matthew Fleming said:

I ran into this same issue but I guess I'm not sure what I'm missing with my fix?

This seems like a pretty simple extension to the filter:

/**
 * A remember me filter which respects the session strategy
 */
public class SessionStrategyBasedRememberMeFilter extends RememberMeAuthenticationFilter {

    private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();

    public SessionStrategyBasedRememberMeFilter(AuthenticationManager authenticationManager, RememberMeServices rememberMeServices, SessionAuthenticationStrategy sessionStrategy) {
        super(authenticationManager, rememberMeServices);
        this.sessionStrategy = sessionStrategy;
    }

    @Override
    protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
        if (sessionStrategy != null){
            sessionStrategy.onAuthentication(authResult, request, response);
        }
    }
}
@spring-issuemaster

Lucas Ward said:

Matthew,

I believe I tried that as the very first thing, but ran into issues. It's been awhile, so I can't remember. It's possible I missed something simple though.

If memory serves. The big problem was always the session timeout issue, and in my testing it didn't work. But, I was a bit under the gun at the time. Have you tried setting session timeout in a dev instance really low and playing around with it? What happened in the scenarios where the first logged in user was timed out, but then a second logs in, and then the first comes back again, but is this time authenticated with a token auth, rather than username and password? Or if both of them timeout and come back? I was never able to get that to work with the simple filter extension you've done. (again, could have made a mistake...)

@spring-issuemaster

Matthew Fleming said:

Here's the test cases that I have run through and have been successful with. I'm running tomcat 7 with the "preserve sessions across restarts" turned off. This is because the the concurrency filter doesn't work properly (serialize) when the option is on.

  • start chrome, login (w/remember me). start firefox, login (with remember me). Verify chrome browser is invalided.
  • start chrome, login (w/remember me). close chrome. start firefox, login (w/remember me). close firefox. Open chrome, open firefox, verify chrome invalidated.
  • start chrome, login (w/remember me). close chrome. start firefox, login (w/remember me). close firefox. bounce tomcat. open firefox, open chrome, verify firefox invalidated.

I think the key to this is also the invalidation of the session when the concurrent filter fires. I do this by specifying our logout uri as the 'expired url'.

Here is our config:

    @Bean
    public ConcurrentSessionFilter getConcurrencyFilter() {
        // go to the logout page or the remember me will override the concurrent logout
        return new ConcurrentSessionFilter(getSessionRegistry(), "/j_spring_security_logout");
    }

    @Bean
    public SessionFixationProtectionStrategy getSessionFixationProtectionStrategy() {
        ConcurrentSessionControlStrategy ret = new ConcurrentSessionControlStrategy(getSessionRegistry());
        ret.setMaximumSessions(1);
        return ret;
    }

    @Bean
    public SessionRegistry getSessionRegistry() {
        return new SessionRegistryImpl();
    }
@spring-issuemaster

marc schipperheyn said:

I struggled with this a while ago, left it, and came back to it today. On Stackoverflow Luke Taylor specifies this as simple and possible: http://stackoverflow.com/questions/10587881/spring-security-sessionregistry-with-persistenttokenbasedremembermeservices I'm a little confused about the status of this issue. Also b/c it's kidn of hard to believe that this would not be there.

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