mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-31 06:38:42 +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.AuthenticationException; | ||||||
| import org.springframework.security.AuthenticationManager; | import org.springframework.security.AuthenticationManager; | ||||||
| import org.springframework.security.util.RedirectUtils; | import org.springframework.security.util.RedirectUtils; | ||||||
|  | import org.springframework.security.util.SessionUtils; | ||||||
| 
 | 
 | ||||||
| import org.springframework.security.context.SecurityContextHolder; | 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 + "'"); |             logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         if (invalidateSessionOnSuccessfulAuthentication) { |         if (invalidateSessionOnSuccessfulAuthentication) { | ||||||
|             startNewSessionIfRequired(request); |             SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, null); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         String targetUrl = determineTargetUrl(request); |         String targetUrl = determineTargetUrl(request); | ||||||
| @ -376,53 +376,6 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl | |||||||
|         sendRedirect(request, response, targetUrl); |         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) { |     protected String determineTargetUrl(HttpServletRequest request) { | ||||||
|         // Don't attempt to obtain the url from the saved request if alwaysUsedefaultTargetUrl is set |         // Don't attempt to obtain the url from the saved request if alwaysUsedefaultTargetUrl is set | ||||||
|     	String targetUrl = alwaysUseDefaultTargetUrl ? null :  |     	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 &= | 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". |     ## 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}? |     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 = | intercept-url = | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user