本文主要講一下session fixation attacks以及spring security對它的防範。php
會話固定攻擊,是利用那些登陸前和登陸以後sessionId沒有變化的漏洞來獲取登陸態,進而獲取用戶的相關信息等。java
servlet3.1規範中,HttpServletRequest.java明確規定了一個changeSessionId的方法
tomcat-embed-core-8.5.23-sources.jar!/javax/servlet/http/HttpServletRequest.javagit
/** * 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();
spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/authentication/session/SessionAuthenticationStrategy.javaweb
/** * 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 提供了SessionAuthenticationStrategy接口,用來在登錄成功以後的處理session相關邏輯,它有個抽象類AbstractSessionFixationProtectionStrategy
spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/authentication/session/AbstractSessionFixationProtectionStrategy.javaspring
/** * 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); } }
若是是servlet3.1的話,則spring security默認的SessionAuthenticationStrategy就是ChangeSessionIdAuthenticationStrategy
spring-security-config-4.2.3.RELEASE-sources.jar!/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.javaapache
/** * 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(); } }
spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/authentication/session/ChangeSessionIdAuthenticationStrategy.javatomcat
/** * 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
tomcat-embed-core-8.5.23-sources.jar!/org/apache/catalina/connector/Request.javasession
/** * 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)
tomcat-embed-core-8.5.23-sources.jar!/org/apache/catalina/session/ManagerBase.javaapp
@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; }
tomcat-embed-core-8.5.23-sources.jar!/org/apache/catalina/util/StandardSessionIdGenerator.javaless
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。