Question

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?

Was it helpful?

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 );            
        }
    }
}

OTHER TIPS

You can achieve the same effect by setting a custom random cookie when the user authenticates and checking it each time you receive a request from the user.

Write a custom AuthenticationSuccessStrategy to set the cookie, something like:

public class MySessionAS extends SavedRequestAwareAuthenticationSuccessHandler {

    /**
     * Called after the user has successfully logged in.
     */
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) {
        // Generate random value
        String myAuthToken = ...
        request.getSession().setAttribute("myAuthToken", myAuthToken);
        Cookie myAuthCookie = new Cookie("myAppCookie", myAuthToken);
        myAuthCookie.setSecure(true); // You're using HTTPS, right?
        myAuthCookie.setMaxAge(-1); // It's a session cookie.
        response.addCookie(cookie);
        super.onAuthenticationSuccess(request, response, authentication);
    }
}

and plug that into your form-login configuration.

Then you just need to check the value. A good place to do that is in a custom voter in your web AccessDecisionManager, or if that sounds too complicated, you could just use a custom filter which checks if a user is authenticated (non-empty SecurityContext) and if they are, makes sure the value of the cookie submitted matches the value stored in the session.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top