spring-projects / spring-security

Spring Security
http://spring.io/projects/spring-security
Apache License 2.0
8.82k stars 5.9k forks source link

Session authentication strategy is not called after successfully authentication #4212

Open anschnapp opened 7 years ago

anschnapp commented 7 years ago

Summary

Session authentication strategy is not called after a successfully authentication.

I have used the standard strategy against session fixation, but this is not called.

Actual Behavior

This issue appears if:

  1. The user makes a regular request and get a session.
  2. The user makes a regular second request with this session.
  3. The user makes a authentication with this session.

As a result: Session authentication strategy is not called after successfully authentication.

Expected Behavior

Session authentication strategy is called after successfully authentication.

Configuration

I have configured spring security with the default filter chain (normal order)

Special to mention for the described error: I use the org.springframework.security.web.context.SecurityContextPersistenceFilter as the first filter in the chain. I have used the standard SessionManagementFilter (which is configured with SessionFixationProtectionStrategy).

Version

4.2.1 RELEASE

Details of the cause

I have debugged the behavior and I think I have found the root cause.

After the filter chain was proceeded the SecurityContextPersistenceFilter invoke a saveContext on the configured SecurityContextRepository. (this happens every time and is located in a finally block). As you can see here:

SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
try {
    SecurityContextHolder.setContext(contextBeforeChainExecution);

    chain.doFilter(holder.getRequest(), holder.getResponse());

}
finally {
    SecurityContext contextAfterChainExecution = SecurityContextHolder
            .getContext();
    // Crucial removal of SecurityContextHolder contents - do this before anything
    // else.
    SecurityContextHolder.clearContext();
    repo.saveContext(contextAfterChainExecution, holder.getRequest(),
            holder.getResponse());
    request.removeAttribute(FILTER_APPLIED);

    if (debug) {
        logger.debug("SecurityContextHolder now cleared, as request processing completed");
    }
}

The standard implementation of the repository is the HttpSessionSecurityContextRepository. This one saves a given security context to the session. In this case this is a SecurityContext with an null authentication inside. (will be result from repo.loadContext(holder) if there is no context yet).

This happens on each normal requests. (it seems that this mechanism don't make a session for itself but the state is saved is there was a given session in the request)

So if there was some normal requests there is an empty (not null) security context in the session.

And if this is the case the logic of SessionManagementFilter will not work properly. The configured session authentication strategy will only invoke if the following if statement is true:

if (!securityContextRepository.containsContext(request)) {

This function just checks if there is something in the session and is not null

public boolean containsContext(HttpServletRequest request) {
        HttpSession session = request.getSession(false);

        if (session == null) {
            return false;
        }

        return session.getAttribute(springSecurityContextKey) != null;
    }

But there is an empty not null security context in the session and so the logic inside the if will not be executed and no session invalidation will be invoked.

anschnapp commented 7 years ago

Hi, I have done some further investigation on the issue.

I have tried to make a fix for the SessionManagementFilter so it will work even if there is a security context with empty authentication.

But on testing my fix things seems different.

For authentication I use a UsernamePasswordAuthenticationFilter which inherits from AbstractAuthenticationProcessingFilter. This filter will not call the other filter chain if it's authenticate successful. So the SessionManagementFilter is not invoked for the call where the authentication succeed. Of course the SecurityContextPersistenceFilter will save the security context after the authentication (inside the finally block).

So the session authentication strategy (potential session fixation protection) inside of SessionManagementFilter will never be invoked (even with my "fixed" version).

I have seen that the AbstractAuthenticationProcessingFilter have the possibility to make a session authentication strategy on it's own. But this is deactivate by default. I have set the session fixation protection strategy inside of the AbstractAuthenticationProcessingFilter, and now it works.

But I would recommend to not close this ticket yet (even if my personal problem is solved now)

In the documentation I have read that the SessionManagementFilter should handle such things like session invalidation. But as I already described this don't seem to work. So for me it looks like it's likely that this failure is not only related to me.

And if it would work why is there an additional possibility to configure it on AbstractAuthenticationProcessingFilter level? Isn't this a redundancy of which filter is responsible for such a security concern? IMHO there should be only one filter which is concerned for session auth strategy and this filter should be configured for a session fixation protection strategy.

nyilmaz commented 7 years ago

In javadoc of SessionManagementFilter;

 * Detects that a user has been authenticated since the start of the request and, if they
 * have, calls the configured {@link SessionAuthenticationStrategy} to perform any
 * session-related activity such as activating session-fixation protection mechanisms or
 * checking for multiple concurrent logins.

However as @git2snap stated, a successful authentication does not end up calling SessionManagementFilter since AbstractAuthenticationProcessingFilter prevents executing remaining filters in security filter chain.

What would be the correct setup for using session concurrency management?

Lzw2016 commented 6 years ago

我也遇到相同的问题,SessionManagementFilter 中的 sessionAuthenticationStrategy 不起作用。配置控制并发认证(登录)的代码也不能起作用,如下代码

http.sessionManagement()
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry)
我的解决方案继承AbstractAuthenticationProcessingFilter类重写successfulAuthentication方法
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
            throws IOException, ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
        }
        SecurityContextHolder.getContext().setAuthentication(authResult);
        this.getRememberMeServices().loginSuccess(request, response, authResult);
        // Fire event
        if (this.eventPublisher != null) {
            eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
        }
        chain.doFilter(request, response); // 调用过滤器链
        this.getSuccessHandler().onAuthenticationSuccess(request, response, authResult);
    }

手动调用 chain.doFilter(request, response); 解决问题

我的疑问

  1. 这是spring-security的一个BUG吗?如果不是,为什么要这样设计?
  2. 我这样做有没有其他影响和副作用?
elmuerte commented 5 years ago

This just took me 2 hours of debugging :/

jukkasi commented 5 years ago

Having same issue, when UsernamePasswordAuthenticationFilter is used SessionManagementFilter never calls sessionAuthenticationStrategy.onAuthentication because securityContextRepository.containsContext(request) is never false

berniegp commented 4 years ago

I have the same issue described by @jukkasi with the default configuration in Spring Boot and

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
    }
}

The sequence of events is:

  1. Authenticate "user1" with Authorization: Basic XXX => Set-Cookie: JSESSIONID=ABC
  2. Authenticate "user2" with Authorization: Basic YYY and Cookie: JSESSIONID=ABC => no Set-Cookie header returned.
  3. Make another request with Cookie: JSESSIONID=ABC and without Authorization => session ABC is now associated with the principal of "user2".

My understanding is that this is a textbook example of a session fixation attack which is very worrisome.

As mentioned above, the decision point for this behavior is SessionManagementFilter#doFilter(ServletRequest req, ServletResponse res, FilterChain chain) where if (!securityContextRepository.containsContext(request)) is never false

I also tried with this configuration which made no difference (because of SessionManagementFilter) : http.sessionManagement().sessionFixation().newSession()