Talk about sessionfixations

  Safety

Order

This article mainly talks about the prevention of session fixation attacks and spring security.

session fixation attacks

Session fixed attack is to obtain login status by exploiting loopholes that have no change in sessionId before and after login, and then obtain relevant information of users, etc.

Servlet3.1 specification

In the servlet3.1 specification, HttpServletRequest.java explicitly specifies a method to changeSessionId
tomcat-embed-core-8.5.23-sources.jar! /javax/servlet/http/HttpServletRequest.java

    /**
     * Changes the session ID of the session associated with this request. This
     * method does not create a new session object it only changes the ID of the
     * current session.
     *
     * @return the new session ID allocated to the session
     * @see HttpSessionIdListener
     * @since Servlet 3.1
     */
    public String changeSessionId();

SessionAuthenticationStrategy

spring-security-web-4.2.3.RELEASE-sources.jar! /org/springframework/security/web/authentication/session/SessionAuthenticationStrategy.java

/**
 * Allows pluggable support for HttpSession-related behaviour when an authentication
 * occurs.
 * <p>
 * Typical use would be to make sure a session exists or to change the session Id to guard
 * against session-fixation attacks.
 *
 * @author Luke Taylor
 * @since
 */
public interface SessionAuthenticationStrategy {

    /**
     * Performs Http session-related functionality when a new authentication occurs.
     *
     * @throws SessionAuthenticationException if it is decided that the authentication is
     * not allowed for the session. This will typically be because the user has too many
     * sessions open at once.
     */
    void onAuthentication(Authentication authentication, HttpServletRequest request,
            HttpServletResponse response) throws SessionAuthenticationException;

}

Spring security provides the SessionAuthenticationStrategy interface for handling session-related logic after successful login. It has an abstract class AbstractSessionFixationProtectionStrategy

AbstractSessionFixationProtectionStrategy

spring-security-web-4.2.3.RELEASE-sources.jar! /org/springframework/security/web/authentication/session/AbstractSessionFixationProtectionStrategy.java

    /**
     * Called when a user is newly authenticated.
     * <p>
     * If a session already exists, and matches the session Id from the client, a new
     * session will be created, and the session attributes copied to it (if
     * {@code migrateSessionAttributes} is set). If the client's requested session Id is
     * invalid, nothing will be done, since there is no need to change the session Id if
     * it doesn't match the current session.
     * <p>
     * If there is no session, no action is taken unless the {@code alwaysCreateSession}
     * property is set, in which case a session will be created if one doesn't already
     * exist.
     */
    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()) {

            String originalSessionId;
            String newSessionId;
            Object mutex = WebUtils.getSessionMutex(session);
            synchronized (mutex) {
                // We need to migrate to a new session
                originalSessionId = session.getId();

                session = applySessionFixation(request);
                newSessionId = session.getId();
            }

            if (originalSessionId.equals(newSessionId)) {
                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");
            }

            onSessionChange(originalSessionId, session, authentication);
        }
    }

If servlet3.1 is used, spring security’s default SessionAuthenticationStrategy is ChangeSessionAuthenticationStrategy.

SessionManagementConfigurer

spring-security-config-4.2.3.RELEASE-sources.jar! /org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java

    /**
     * Creates the default {@link SessionAuthenticationStrategy} for session fixation
     * @return the default {@link SessionAuthenticationStrategy} for session fixation
     */
    private static SessionAuthenticationStrategy createDefaultSessionFixationProtectionStrategy() {
        try {
            return new ChangeSessionIdAuthenticationStrategy();
        }
        catch (IllegalStateException e) {
            return new SessionFixationProtectionStrategy();
        }
    }

ChangeSessionIdAuthenticationStrategy

spring-security-web-4.2.3.RELEASE-sources.jar! /org/springframework/security/web/authentication/session/ChangeSessionIdAuthenticationStrategy.java

/**
 * Uses {@code HttpServletRequest.changeSessionId()} to protect against session fixation
 * attacks. This is the default implementation for Servlet 3.1+.
 *
 * @author Rob Winch
 * @since 3.2
 */
public final class ChangeSessionIdAuthenticationStrategy
        extends AbstractSessionFixationProtectionStrategy {
    private final Method changeSessionIdMethod;

    public ChangeSessionIdAuthenticationStrategy() {
        Method changeSessionIdMethod = ReflectionUtils
                .findMethod(HttpServletRequest.class, "changeSessionId");
        if (changeSessionIdMethod == null) {
            throw new IllegalStateException(
                    "HttpServletRequest.changeSessionId is undefined. Are you using a Servlet 3.1+ environment?");
        }
        this.changeSessionIdMethod = changeSessionIdMethod;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.springframework.security.web.authentication.session.
     * AbstractSessionFixationProtectionStrategy
     * #applySessionFixation(javax.servlet.http.HttpServletRequest)
     */
    @Override
    HttpSession applySessionFixation(HttpServletRequest request) {
        ReflectionUtils.invokeMethod(this.changeSessionIdMethod, request);
        return request.getSession();
    }
}

Call the changeSessionId method through reflection, specifically call Request#changeSessionId.

Request#changeSessionId

tomcat-embed-core-8.5.23-sources.jar! /org/apache/catalina/connector/Request.java

    /**
     * Changes the session ID of the session associated with this request.
     *
     * @return the old session ID before it was changed
     * @see javax.servlet.http.HttpSessionIdListener
     * @since Servlet 3.1
     */
    @Override
    public String changeSessionId() {

        Session session = this.getSessionInternal(false);
        if (session == null) {
            throw new IllegalStateException(
                sm.getString("coyoteRequest.changeSessionId"));
        }

        Manager manager = this.getContext().getManager();
        manager.changeSessionId(session);

        String newSessionId = session.getId();
        this.changeSessionId(newSessionId);

        return newSessionId;
    }

Changesession id (session) is called here.

ManagerBase#changeSessionId(session)

tomcat-embed-core-8.5.23-sources.jar! /org/apache/catalina/session/ManagerBase.java

    @Override
    public void changeSessionId(Session session) {
        String newId = generateSessionId();
        changeSessionId(session, newId, true, true);
    }
    protected void changeSessionId(Session session, String newId,
            boolean notifySessionListeners, boolean notifyContainerListeners) {
        String oldId = session.getIdInternal();
        session.setId(newId, false);
        session.tellChangedSessionId(newId, oldId,
                notifySessionListeners, notifyContainerListeners);
    }

    /**
     * Generate and return a new session identifier.
     * @return a new session id
     */
    protected String generateSessionId() {

        String result = null;

        do {
            if (result != null) {
                // Not thread-safe but if one of multiple increments is lost
                // that is not a big deal since the fact that there was any
                // duplicate is a much bigger issue.
                duplicates++;
            }

            result = sessionIdGenerator.generateSessionId();

        } while (sessions.containsKey(result));

        return result;
    }

StandardSessionIdGenerator#generateSessionId

tomcat-embed-core-8.5.23-sources.jar! /org/apache/catalina/util/StandardSessionIdGenerator.java

public class StandardSessionIdGenerator extends SessionIdGeneratorBase {

    @Override
    public String generateSessionId(String route) {

        byte random[] = new byte[16];
        int sessionIdLength = getSessionIdLength();

        // Render the result as a String of hexadecimal digits
        // Start with enough space for sessionIdLength and medium route size
        StringBuilder buffer = new StringBuilder(2 * sessionIdLength + 20);

        int resultLenBytes = 0;

        while (resultLenBytes < sessionIdLength) {
            getRandomBytes(random);
            for (int j = 0;
            j < random.length && resultLenBytes < sessionIdLength;
            j++) {
                byte b1 = (byte) ((random[j] & 0xf0) >> 4);
                byte b2 = (byte) (random[j] & 0x0f);
                if (b1 < 10)
                    buffer.append((char) ('0' + b1));
                else
                    buffer.append((char) ('A' + (b1 - 10)));
                if (b2 < 10)
                    buffer.append((char) ('0' + b2));
                else
                    buffer.append((char) ('A' + (b2 - 10)));
                resultLenBytes++;
            }
        }

        if (route != null && route.length() > 0) {
            buffer.append('.').append(route);
        } else {
            String jvmRoute = getJvmRoute();
            if (jvmRoute != null && jvmRoute.length() > 0) {
                buffer.append('.').append(jvmRoute);
            }
        }

        return buffer.toString();
    }
}

This is tomcat’s logic for generating sessionId.

Summary

Spring security uses SessionAuthenticationStrategy to process related session after logging in successfully. If servlet is 3.1+,use ChangeSessionAuthenticationStrategy to replace sessionId to prevent sessionFixationAttachments.

doc