SEC-834: Session fixation attack protection will cause problems with URL rewriting

http://jira.springframework.org/browse/SEC-834. Changed position of SessionFixationProtectionFilter and modified it to make a decision about whether authentication has taken place prior to calling doFilter(). Previously it did this on the return through the filter chain, which caused the problem described in this issue.
This commit is contained in:
Luke Taylor
2008-05-15 00:26:27 +00:00
parent 7f38c656ca
commit d17a2da9e0
4 changed files with 38 additions and 194 deletions

View File

@@ -22,8 +22,7 @@ public abstract class FilterChainOrder {
public static final int CHANNEL_FILTER = FILTER_CHAIN_FIRST;
public static final int CONCURRENT_SESSION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int HTTP_SESSION_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int SESSION_FIXATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int HTTP_SESSION_CONTEXT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int LOGOUT_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int X509_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int PRE_AUTH_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
@@ -37,6 +36,7 @@ public abstract class FilterChainOrder {
public static final int ANONYMOUS_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int EXCEPTION_TRANSLATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int NTLM_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int SESSION_FIXATION_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int FILTER_SECURITY_INTERCEPTOR = FILTER_CHAIN_FIRST + INTERVAL * i++;
public static final int SWITCH_USER_FILTER = FILTER_CHAIN_FIRST + INTERVAL * i++;
@@ -46,7 +46,6 @@ public abstract class FilterChainOrder {
filterNameToOrder.put("FIRST", new Integer(Integer.MIN_VALUE));
filterNameToOrder.put("CHANNEL_FILTER", new Integer(CHANNEL_FILTER));
filterNameToOrder.put("CONCURRENT_SESSION_FILTER", new Integer(CONCURRENT_SESSION_FILTER));
filterNameToOrder.put("SESSION_CONTEXT_INTEGRATION_FILTER", new Integer(HTTP_SESSION_CONTEXT_FILTER));
filterNameToOrder.put("LOGOUT_FILTER", new Integer(LOGOUT_FILTER));
filterNameToOrder.put("X509_FILTER", new Integer(X509_FILTER));
filterNameToOrder.put("PRE_AUTH_FILTER", new Integer(PRE_AUTH_FILTER));
@@ -59,6 +58,7 @@ public abstract class FilterChainOrder {
filterNameToOrder.put("ANONYMOUS_FILTER", new Integer(ANONYMOUS_FILTER));
filterNameToOrder.put("EXCEPTION_TRANSLATION_FILTER", new Integer(EXCEPTION_TRANSLATION_FILTER));
filterNameToOrder.put("NTLM_FILTER", new Integer(NTLM_FILTER));
filterNameToOrder.put("SESSION_CONTEXT_INTEGRATION_FILTER", new Integer(HTTP_SESSION_CONTEXT_FILTER));
filterNameToOrder.put("FILTER_SECURITY_INTERCEPTOR", new Integer(FILTER_SECURITY_INTERCEPTOR));
filterNameToOrder.put("SWITCH_USER_FILTER", new Integer(SWITCH_USER_FILTER));
filterNameToOrder.put("LAST", new Integer(Integer.MAX_VALUE));

View File

@@ -6,24 +6,23 @@ import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
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.HttpSessionContextIntegrationFilter;
import org.springframework.security.context.SecurityContext;
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.
* <p>
* If the response has already been committed when the filter checks the authentication state, then it isn't possible
* to create a new session and the filter will print a warning to that effect.
* 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
@@ -55,22 +54,17 @@ public class SessionFixationProtectionFilter extends SpringSecurityFilter {
}
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;
}
HttpSession session = request.getSession();
SecurityContext sessionSecurityContext =
(SecurityContext) session.getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY);
SessionFixationProtectionResponseWrapper wrapper =
new SessionFixationProtectionResponseWrapper(response, request);
try {
chain.doFilter(request, wrapper);
} finally {
if (!wrapper.isNewSessionStarted()) {
startNewSessionIfRequired(request, response);
}
if (sessionSecurityContext == null && isAuthenticated()) {
// The user has been authenticated during the current request, so do the session migration
startNewSessionIfRequired(request, response);
}
chain.doFilter(request, response);
}
private boolean isAuthenticated() {
@@ -92,83 +86,12 @@ public class SessionFixationProtectionFilter extends SpringSecurityFilter {
}
/**
* Called when the an initially unauthenticated request completes or a redirect or sendError occurs.
* Called when the a user wasn't authenticated at the start of the request but has been during it
* <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.
* 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, HttpServletResponse response) {
if (isAuthenticated()) {
if (request.getSession(false) != null && response.isCommitted()) {
logger.warn("Response is already committed. Unable to create new session.");
}
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.
* <p>
* Only used to wrap the response if the conditions are right at the start of the request to potentially
* require starting a new session, i.e. that the user isn't authenticated and a session existed to begin with.
*/
class SessionFixationProtectionResponseWrapper extends HttpServletResponseWrapper {
private HttpServletRequest request;
private boolean newSessionStarted;
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);
}
public void flushBuffer() throws IOException {
startNewSession();
super.flushBuffer();
}
/**
* Calls <code>startNewSessionIfRequired()</code>
*/
private void startNewSession() {
if (newSessionStarted) {
return;
}
startNewSessionIfRequired(request, this);
newSessionStarted = true;
}
boolean isNewSessionStarted() {
return newSessionStarted;
}
protected void startNewSessionIfRequired(HttpServletRequest request, HttpServletResponse response) {
SessionUtils.startNewSessionIfRequired(request, migrateSessionAttributes, sessionRegistry);
}
}