聊聊session fixation attacks

本文主要讲一下session fixation attacks以及spring security对它的防范。

session fixation attacks

会话固定攻击,是利用那些登录前和登录之后sessionId没有变化的漏洞来获取登录态,进而获取用户的相关信息等。

servlet3.1规范

servlet3.1规范中,HttpServletRequest.java明确规定了一个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.
 * 

* 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 提供了SessionAuthenticationStrategy接口,用来在登陆成功之后的处理session相关逻辑,它有个抽象类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.
     * 

* 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. *

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

如果是servlet3.1的话,则spring security默认的SessionAuthenticationStrategy就是ChangeSessionIdAuthenticationStrategy

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();
    }
}
通过反射调用changeSessionId方法,具体是调用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;
    }
这里调用了manager.changeSessionId(session)

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();
    }
}
这段是tomcat生成sessionId的逻辑

小结

spring security通过SessionAuthenticationStrategy,在登录成功之后进行相关session处理,如果servlet3.1+,则使用ChangeSessionIdAuthenticationStrategy来更换sessionId,以防范session fixation attacks。

doc

  • Session fixation
  • 会话固定攻击

你可能感兴趣的:(安全)