Spring Security如何防止会话固定攻击(session fixation attack)
Session fixation attack(会话固定攻击)是利用服务器的session不变机制,借他人之手获得认证和授权,然后冒充他人。如果应用程序在用户首次访问它时为每一名用户建立一个匿名会话,这时往往就会出现会话固定漏洞。然后,一旦用户登录,该会话即升级为通过验证的会话。最初,会话令牌并未被赋予任何访问权限,但在用户通过验证后,这个令牌也具有了该用户的访问权限。
防止会话固定攻击,可以在用户登录成功后重新创建一个session id,并将登录前的匿名会话强制失效。Spring Security默认即可防止会话固定攻击。具体实现方式[3.1版本]如下:HttpConfigurationBuilder的createSessionManagementFilters方法用于配置文件中的session-management属性,并根据配置创建SessionManagementFilter。其首先读取session-fixation-protection并存入sessionFixationAttribute变量,随后,通过如下语句判断是否需要进行会话规定漏洞保护
boolean sessionFixationProtectionRequired = !sessionFixationAttribute.equals(OPT_SESSION_FIXATION_NO_PROTECTION);
需要说明的是在上述语句之前,如果发现sessionFixationAttribute变量没有赋值(例如,没有配置session-management属性),程序会采用如下语句确保缺省的session-fixation-protection=migrateSession
if (!StringUtils.hasText(sessionFixationAttribute)) { sessionFixationAttribute = OPT_SESSION_FIXATION_MIGRATE_SESSION;
下面的判断,如果您配置了session-management并启用了concurrency-control,则向SessionManagementFilter注册ConcurrentSessionControlStrategy类;否则如果上面代码中的sessionFixationProtectionRequired = true或者配置了invalid-session-url,则向SessionManagementFilter注册SessionFixationProtectionStrategy类,由于ConcurrentSessionControlStrategy继承自SessionFixationProtectionStrategy,因此以上条件下,都会导致SessionFixationProtectionStrategy的策略被应用,而SessionFixationProtectionStrategy的策略,将在SessionManagementFilter中被执行,代码如下
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; if (request.getAttribute("__spring_security_session_mgmt_filter_applied") != null) { chain.doFilter(request, response); return; } request.setAttribute("__spring_security_session_mgmt_filter_applied", Boolean.TRUE); if (!this.securityContextRepository.containsContext(request)) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if ((authentication != null) && (!this.authenticationTrustResolver.isAnonymous(authentication))) { try { this.sessionAuthenticationStrategy.onAuthentication(authentication, request, response); ......
回头来看SessionFixationProtectionStrategy的onAuthentication方法,如下代码中,字体加粗部分,先设置当前的session失效,再创建一个新的session
public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) { boolean hadSessionAlready = request.getSession(false) != null; if ((!hadSessionAlready) && (!this.alwaysCreateSession)) { return; } HttpSession session = request.getSession(); if ((hadSessionAlready) && (request.isRequestedSessionIdValid())) { String originalSessionId = session.getId(); if (this.logger.isDebugEnabled()) { this.logger.debug("Invalidating session with Id '" + originalSessionId + "' " + (this.migrateSessionAttributes ? "and" : "without") + " migrating attributes."); } Map attributesToMigrate = extractAttributes(session); session.invalidate();
session = request.getSession(true); if (this.logger.isDebugEnabled()) { this.logger.debug("Started new session: " + session.getId()); } if (originalSessionId.equals(session.getId())) { this.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); onSessionChange(originalSessionId, session, authentication); } }
最后,怎么关闭Spring Security的session-fixation-protection呢,由以上代码分析知,除了将session-fixation-protection设置为null以外,还不能设置诸如concurrency-control或invalid-session-url属性,举例如下
<session-management session-fixation-protection="none" />