Search code examples
jbossspring-securitycdijboss-weldsession-fixation

Spring's SessionFixationProtectionStrategy kills CDI Conversation under JBoss / Weld


I have an object that's marked as @ConversationScoped and is used between many steps of a wizard.

This works perfectly except that when my user's are login in, the SessionFixationProtectionStrategy of Spring calls the session.invalidate() method to recreate a new session with a new id. It then goes and reattach the attributes of the invalidated session to the new one.

The problem is that there's a WeldListener instance that is bound to the sessionDestroyed event and that will kill @ConversationScoped instances that are bound to the HttpSession object.

I have disabled the SessionFixationProtectionStrategy and am now using NullAuthenticatedSessionStrategy which does nothing, but I would still like to keep the Session Fixation strategy to protect my site from this.

Any suggestions on how to work around this?


Solution

  • Here's a strategy I'm using:

    1. Detach the weld contexts (session and conversation) from the current session.
    2. After copying all session attributes, remove them from the session before invalidating it (for some reason not doing this doesn't work, probably a session destruction listener somewhere in weld)
    3. Create new session, copy in attributes from previous session
    4. Reattach weld contexts.

    You need to copy the SessionFixationProtectionStrategy class to be able to implement this since there are no appropriate hooks already in place. Here's the onAuthenticate.

    public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) {
            boolean hadSessionAlready = request.getSession(false) != null;
    
            if (!hadSessionAlready && !alwaysCreateSession) {
                // Session fixation isn't a problem if there's no session
    
                return;
            }
    
            // Create new session if necessary
            HttpSession session = request.getSession();
    
            if (hadSessionAlready && request.isRequestedSessionIdValid()) {
                // We need to migrate to a new session
                String originalSessionId = session.getId();
    
                if (logger.isDebugEnabled()) {
                    logger.debug("Invalidating session with Id '" + originalSessionId +"' " + (migrateSessionAttributes ?
                            "and" : "without") +  " migrating attributes.");
                }
    
                String id = weldAwareSessionFixationProtectionStrategyHelper.beforeInvalidateSession( request );
    
                Map<String, Object> attributesToMigrate = extractAttributes(session);
    
                for( String key : attributesToMigrate.keySet() ) {
                    session.removeAttribute( key );
                }
    
    
                session.invalidate();
                session = request.getSession(true); // we now have a new session
    
                if (logger.isDebugEnabled()) {
                    logger.debug("Started new session: " + session.getId());
                }
    
    
                if (originalSessionId.equals(session.getId())) {
                    logger.warn("Your servlet container did not change the session ID when a new session was     created. You will" +
                            " not be adequately protected against session-fixation attacks");
                }
    
                transferAttributes(attributesToMigrate, session);
    
                weldAwareSessionFixationProtectionStrategyHelper.afterCreateNewSession( request, id );
    
                onSessionChange(originalSessionId, session, authentication);
            }
        }
    

    ... and here's the WeldAwareSessionFixationProtectionStrategyHelper

    @ApplicationScoped
    public class WeldAwareSessionFixationProtectionStrategyHelper {
    
        @Inject
        private HttpSessionContext httpSessionContext;
    
        @Inject
        private HttpConversationContext httpConversationContext;
    
        public String beforeInvalidateSession( HttpServletRequest httpServletRequest ) {
    
            String currentId = null;
    
            if( !httpConversationContext.getCurrentConversation().isTransient() ) {
                currentId = httpConversationContext.getCurrentConversation().getId();
            }
    
            httpConversationContext.deactivate();
            httpConversationContext.dissociate( httpServletRequest );
    
            httpSessionContext.deactivate();
            httpSessionContext.dissociate( httpServletRequest );
    
            return currentId;
        }
    
        public void afterCreateNewSession( HttpServletRequest httpServletRequest, String cid ) {
    
            httpSessionContext.associate( httpServletRequest );
            httpSessionContext.activate();
    
            httpConversationContext.associate( httpServletRequest );
    
            if( cid == null ) {
                httpConversationContext.activate();
            } else {
                httpConversationContext.activate( cid );            
            }
        }
    }