mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +00:00 
			
		
		
		
	SEC-689: Session Fixation protection should be available to all authentication mechanisms.
http://jira.springframework.org/browse/SEC-689. Added a general SessionFixationProtectionFilter which can be added to the filter stack to detect when a user has been authenticated and then migrate them to a new session. Also added support to <http/> namespace element.
This commit is contained in:
		
							parent
							
								
									83bcc6ad7c
								
							
						
					
					
						commit
						8f5bcb64a6
					
				| @ -20,6 +20,7 @@ import org.springframework.security.Authentication; | ||||
| import org.springframework.security.AuthenticationException; | ||||
| import org.springframework.security.AuthenticationManager; | ||||
| import org.springframework.security.util.RedirectUtils; | ||||
| import org.springframework.security.util.SessionUtils; | ||||
| 
 | ||||
| import org.springframework.security.context.SecurityContextHolder; | ||||
| 
 | ||||
| @ -353,9 +354,8 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl | ||||
|             logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'"); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         if (invalidateSessionOnSuccessfulAuthentication) { | ||||
|             startNewSessionIfRequired(request); | ||||
|             SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, null); | ||||
|         } | ||||
| 
 | ||||
|         String targetUrl = determineTargetUrl(request); | ||||
| @ -376,53 +376,6 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl | ||||
|         sendRedirect(request, response, targetUrl); | ||||
|     } | ||||
| 
 | ||||
|     private void startNewSessionIfRequired(HttpServletRequest request) { | ||||
|         HttpSession session = request.getSession(false); | ||||
| 
 | ||||
|         if (session == null) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!migrateInvalidatedSessionAttributes) { | ||||
| 
 | ||||
|             if (logger.isDebugEnabled()) { | ||||
|                 logger.debug("Invalidating session without migrating attributes."); | ||||
|             } | ||||
| 
 | ||||
|             session.invalidate(); | ||||
|             session = null; | ||||
| 
 | ||||
|             // this is probably not necessary, but seems cleaner since | ||||
|             // there already was a session going. | ||||
|             request.getSession(true); | ||||
| 
 | ||||
|         } else { | ||||
| 
 | ||||
|             if (logger.isDebugEnabled()) { | ||||
|                 logger.debug("Invalidating session and migrating attributes."); | ||||
|             } | ||||
| 
 | ||||
|             HashMap migratedAttributes = new HashMap(); | ||||
| 
 | ||||
|             Enumeration enumer = session.getAttributeNames(); | ||||
| 
 | ||||
|             while (enumer.hasMoreElements()) { | ||||
|                 String key = (String) enumer.nextElement(); | ||||
|                 migratedAttributes.put(key, session.getAttribute(key)); | ||||
|             } | ||||
| 
 | ||||
|             session.invalidate(); | ||||
|             session = request.getSession(true); // we now have a new session | ||||
| 
 | ||||
|             Iterator iter = migratedAttributes.entrySet().iterator(); | ||||
| 
 | ||||
|             while (iter.hasNext()) { | ||||
|                 Map.Entry entry = (Map.Entry) iter.next(); | ||||
|                 session.setAttribute((String) entry.getKey(), entry.getValue()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     protected String determineTargetUrl(HttpServletRequest request) { | ||||
|         // Don't attempt to obtain the url from the saved request if alwaysUsedefaultTargetUrl is set | ||||
|     	String targetUrl = alwaysUseDefaultTargetUrl ? null :  | ||||
|  | ||||
| @ -0,0 +1,156 @@ | ||||
| package org.springframework.security.ui; | ||||
| 
 | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| import javax.servlet.FilterChain; | ||||
| import javax.servlet.ServletException; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
| import javax.servlet.http.HttpServletResponseWrapper; | ||||
| 
 | ||||
| import org.springframework.security.Authentication; | ||||
| import org.springframework.security.AuthenticationTrustResolver; | ||||
| import org.springframework.security.AuthenticationTrustResolverImpl; | ||||
| import org.springframework.security.concurrent.SessionRegistry; | ||||
| import org.springframework.security.context.SecurityContextHolder; | ||||
| import org.springframework.security.util.SessionUtils; | ||||
| 
 | ||||
| /** | ||||
|  * Detects that a user has been authenticated since the start of the request and starts a new session. | ||||
|  * <p> | ||||
|  * This is essentially a generalization of the functionality that was implemented for SEC-399. Additionally, it will | ||||
|  * update the configured SessionRegistry if one is in use, thus preventing problems when used with Spring Security's | ||||
|  * concurrent session control. | ||||
|  *  | ||||
|  * @author Martin Algesten | ||||
|  * @author Luke Taylor | ||||
|  * @since 2.0 | ||||
|  */ | ||||
| public class SessionFixationProtectionFilter extends SpringSecurityFilter { | ||||
|     //~ Static fields/initializers ===================================================================================== | ||||
| 
 | ||||
|     static final String FILTER_APPLIED = "__spring_security_session_fixation_filter_applied"; | ||||
|   | ||||
|     //~ Instance fields ================================================================================================ | ||||
| 
 | ||||
|     private SessionRegistry sessionRegistry; | ||||
|      | ||||
|     /** | ||||
|      * Indicates that the session attributes of the session to be invalidated | ||||
|      * should be migrated to the new session. Defaults to <code>true</code>. | ||||
|      */ | ||||
|     private boolean migrateSessionAttributes = true;     | ||||
| 
 | ||||
|     private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();  | ||||
| 
 | ||||
|     protected void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) | ||||
|             throws IOException, ServletException { | ||||
|         // Session fixation isn't a problem if there's no session | ||||
|         if(request.getSession(false) == null || request.getAttribute(FILTER_APPLIED) != null) { | ||||
|             chain.doFilter(request, response); | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         request.setAttribute(FILTER_APPLIED, Boolean.TRUE); | ||||
|                  | ||||
|         if (isAuthenticated()) { | ||||
|             // We don't have to worry about session fixation attack if already authenticated  | ||||
|             chain.doFilter(request, response); | ||||
|             return;             | ||||
|         } | ||||
|          | ||||
|         SessionFixationProtectionResponseWrapper wrapper =  | ||||
|             new SessionFixationProtectionResponseWrapper(response, request); | ||||
|         try { | ||||
|             chain.doFilter(request, wrapper); | ||||
|         } finally { | ||||
|             if (!wrapper.isNewSessionStarted()) { | ||||
|                 startNewSessionIfRequired(request); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     private boolean isAuthenticated() { | ||||
|         Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); | ||||
|          | ||||
|         return authentication != null && !authenticationTrustResolver.isAnonymous(authentication);         | ||||
|     } | ||||
|      | ||||
|     public void setMigrateSessionAttributes(boolean migrateSessionAttributes) { | ||||
|         this.migrateSessionAttributes = migrateSessionAttributes; | ||||
|     } | ||||
| 
 | ||||
|     public int getOrder() { | ||||
|         return FilterChainOrder.SESSION_FIXATION_FILTER; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Called when the an initially unauthenticated request completes or a redirect or sendError occurs. | ||||
|      * <p> | ||||
|      * If the user is now authenticated, a new session will be created, the session attributes copied to it (if  | ||||
|      * <tt>migrateSessionAttributes</tt> is set and the sessionRegistry updated with the new session information. | ||||
|      */ | ||||
|     protected void startNewSessionIfRequired(HttpServletRequest request) { | ||||
|         if (isAuthenticated()) { | ||||
|             SessionUtils.startNewSessionIfRequired(request, migrateSessionAttributes, sessionRegistry); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Response wrapper to handle the situation where we need to migrate the session after a redirect or sendError. | ||||
|      * Similar in function to Martin Algesten's OnRedirectUpdateSessionResponseWrapper used in  | ||||
|      * HttpSessionContextIntegrationFilter.   | ||||
|      */ | ||||
|     private class SessionFixationProtectionResponseWrapper extends HttpServletResponseWrapper { | ||||
|         private HttpServletRequest request; | ||||
|         private boolean newSessionStarted; | ||||
| 
 | ||||
|         public SessionFixationProtectionResponseWrapper(HttpServletResponse response, HttpServletRequest request) { | ||||
|             super(response); | ||||
|             this.request = request; | ||||
|         } | ||||
|          | ||||
|         /** | ||||
|          * Makes sure a new session is created before calling the | ||||
|          * superclass <code>sendError()</code> | ||||
|          */ | ||||
|         public void sendError(int sc) throws IOException { | ||||
|             startNewSession(); | ||||
|             super.sendError(sc); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Makes sure a new session is created before calling the | ||||
|          * superclass <code>sendError()</code> | ||||
|          */ | ||||
|         public void sendError(int sc, String msg) throws IOException { | ||||
|             startNewSession(); | ||||
|             super.sendError(sc, msg); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Makes sure a new session is created before calling the | ||||
|          * superclass <code>sendRedirect()</code> | ||||
|          */ | ||||
|         public void sendRedirect(String location) throws IOException { | ||||
|             startNewSession(); | ||||
|             super.sendRedirect(location); | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * Calls <code>startNewSessionIfRequired()</code> | ||||
|          */ | ||||
|         private void startNewSession() { | ||||
|             if (newSessionStarted) { | ||||
|                 return; | ||||
|             } | ||||
|             startNewSessionIfRequired(request); | ||||
|             newSessionStarted = true; | ||||
|         } | ||||
| 
 | ||||
|         private boolean isNewSessionStarted() { | ||||
|             return newSessionStarted; | ||||
|         } | ||||
|     }     | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,75 @@ | ||||
| package org.springframework.security.util; | ||||
| 
 | ||||
| import java.util.Enumeration; | ||||
| import java.util.HashMap; | ||||
| import java.util.Iterator; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpSession; | ||||
| 
 | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.springframework.security.concurrent.SessionRegistry; | ||||
| import org.springframework.security.concurrent.SessionRegistryUtils; | ||||
| import org.springframework.security.context.SecurityContextHolder; | ||||
| 
 | ||||
| /** | ||||
|  * @author Luke Taylor | ||||
|  * @version $Id$ | ||||
|  * @since 2.0 | ||||
|  */ | ||||
| public final class SessionUtils { | ||||
|     private final static Log logger = LogFactory.getLog(SessionUtils.class); | ||||
|      | ||||
|     SessionUtils() {} | ||||
| 
 | ||||
|     public static void startNewSessionIfRequired(HttpServletRequest request, boolean migrateAttributes,  | ||||
|             SessionRegistry sessionRegistry) { | ||||
|          | ||||
|         HttpSession session = request.getSession(false); | ||||
| 
 | ||||
|         if (session == null) { | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         String originalSessionId = session.getId(); | ||||
| 
 | ||||
|         if (logger.isDebugEnabled()) { | ||||
|             logger.debug("Invalidating session " + (migrateAttributes ? "and" : "without") +  " migrating attributes."); | ||||
|         }         | ||||
| 
 | ||||
|         HashMap attributesToMigrate = null; | ||||
|          | ||||
|         if (migrateAttributes) { | ||||
|             attributesToMigrate = new HashMap(); | ||||
| 
 | ||||
|             Enumeration enumer = session.getAttributeNames(); | ||||
| 
 | ||||
|             while (enumer.hasMoreElements()) { | ||||
|                 String key = (String) enumer.nextElement(); | ||||
|                 attributesToMigrate.put(key, session.getAttribute(key)); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         session.invalidate(); | ||||
|         session = request.getSession(true); // we now have a new session | ||||
| 
 | ||||
|         if (attributesToMigrate != null) { | ||||
|             Iterator iter = attributesToMigrate.entrySet().iterator(); | ||||
| 
 | ||||
|             while (iter.hasNext()) { | ||||
|                 Map.Entry entry = (Map.Entry) iter.next(); | ||||
|                 session.setAttribute((String) entry.getKey(), entry.getValue()); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         if (sessionRegistry != null) { | ||||
|             sessionRegistry.removeSessionInformation(originalSessionId); | ||||
|             Object principal = SessionRegistryUtils.obtainPrincipalFromAuthentication( | ||||
|                     SecurityContextHolder.getContext().getAuthentication()); | ||||
|              | ||||
|             sessionRegistry.registerNewSession(session.getId(), principal); | ||||
|         }  | ||||
|     } | ||||
| } | ||||
| @ -202,6 +202,9 @@ http.attlist &= | ||||
| http.attlist &= | ||||
|     ## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to "Spring Security Application". | ||||
|     attribute realm {xsd:string}? | ||||
| http.attlist &= | ||||
|     ## Indicates whether an existing session should be invalidated when a user authenticates and a new session started. If set to "none" no change will be made. "newSession" will create a new empty session. "migrateSession" will create a new session and copy the session attributes to the new session. Defaults to "migrateSession". | ||||
|     attribute session-fixation-protection {"none" | "newSession" | "migrateSession" }? | ||||
| 
 | ||||
| 
 | ||||
| intercept-url = | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user