Merge branch '5.8.x'

# Conflicts:
#	docs/modules/ROOT/pages/servlet/exploits/csrf.adoc
This commit is contained in:
Steve Riesenberg
2022-10-05 14:46:15 -05:00
7 changed files with 545 additions and 3 deletions

View File

@@ -48,6 +48,7 @@ import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
import org.springframework.security.web.csrf.DefaultCsrfToken;
import org.springframework.security.web.csrf.DeferredCsrfToken;
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
@@ -64,6 +65,7 @@ import org.springframework.web.servlet.support.RequestDataValueProcessor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.BDDMockito.given;
@@ -429,7 +431,7 @@ public class CsrfConfigurerTests {
}
@Test
public void getLoginWhenCsrfTokenRequestHandlerSetThenRespondsWithNormalCsrfToken() throws Exception {
public void getLoginWhenCsrfTokenRequestAttributeHandlerSetThenRespondsWithNormalCsrfToken() throws Exception {
CsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);
CsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token");
given(csrfTokenRepository.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class)))
@@ -444,7 +446,7 @@ public class CsrfConfigurerTests {
}
@Test
public void loginWhenCsrfTokenRequestHandlerSetAndNormalCsrfTokenThenSuccess() throws Exception {
public void loginWhenCsrfTokenRequestAttributeHandlerSetAndNormalCsrfTokenThenSuccess() throws Exception {
CsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token");
CsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);
given(csrfTokenRepository.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class)))
@@ -466,6 +468,47 @@ public class CsrfConfigurerTests {
verifyNoMoreInteractions(csrfTokenRepository);
}
@Test
public void getLoginWhenXorCsrfTokenRequestAttributeHandlerSetThenRespondsWithMaskedCsrfToken() throws Exception {
CsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);
CsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token");
given(csrfTokenRepository.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class)))
.willReturn(new TestDeferredCsrfToken(csrfToken));
CsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository;
CsrfTokenRequestHandlerConfig.HANDLER = new XorCsrfTokenRequestAttributeHandler();
this.spring.register(CsrfTokenRequestHandlerConfig.class, BasicController.class).autowire();
this.mvc.perform(get("/login")).andExpect(status().isOk())
.andExpect(content().string(not(containsString(csrfToken.getToken()))));
verify(csrfTokenRepository).loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class));
verifyNoMoreInteractions(csrfTokenRepository);
}
@Test
public void loginWhenXorCsrfTokenRequestAttributeHandlerSetAndMaskedCsrfTokenThenSuccess() throws Exception {
CsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token");
CsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);
given(csrfTokenRepository.loadDeferredToken(any(HttpServletRequest.class), any(HttpServletResponse.class)))
.willReturn(new TestDeferredCsrfToken(csrfToken));
CsrfTokenRequestHandlerConfig.REPO = csrfTokenRepository;
CsrfTokenRequestHandlerConfig.HANDLER = new XorCsrfTokenRequestAttributeHandler();
this.spring.register(CsrfTokenRequestHandlerConfig.class, BasicController.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/login")).andReturn();
CsrfToken csrfTokenAttribute = (CsrfToken) mvcResult.getRequest().getAttribute(CsrfToken.class.getName());
// @formatter:off
MockHttpServletRequestBuilder loginRequest = post("/login")
.header(csrfToken.getHeaderName(), csrfTokenAttribute.getToken())
.param("username", "user")
.param("password", "password");
// @formatter:on
this.mvc.perform(loginRequest).andExpect(redirectedUrl("/"));
verify(csrfTokenRepository).saveToken(isNull(), any(HttpServletRequest.class), any(HttpServletResponse.class));
verify(csrfTokenRepository, times(3)).loadDeferredToken(any(HttpServletRequest.class),
any(HttpServletResponse.class));
verifyNoMoreInteractions(csrfTokenRepository);
}
@Configuration
static class AllowHttpMethodsFirewallConfig {

View File

@@ -300,6 +300,39 @@ public class CsrfConfigTests {
// @formatter:on
}
@Test
public void postWhenUsingCsrfAndXorCsrfTokenRequestProcessorThenOk() throws Exception {
this.spring.configLocations(this.xml("WithXorCsrfTokenRequestAttributeHandler"), this.xml("shared-controllers"))
.autowire();
// @formatter:off
MvcResult mvcResult = this.mvc.perform(get("/ok"))
.andExpect(status().isOk())
.andReturn();
MockHttpSession session = (MockHttpSession) mvcResult.getRequest().getSession();
CsrfToken csrfToken = (CsrfToken) mvcResult.getRequest().getAttribute("_csrf");
MockHttpServletRequestBuilder ok = post("/ok")
.header(csrfToken.getHeaderName(), csrfToken.getToken())
.session(session);
this.mvc.perform(ok).andExpect(status().isOk());
// @formatter:on
}
@Test
public void postWhenUsingCsrfAndXorCsrfTokenRequestProcessorWithRawTokenThenForbidden() throws Exception {
this.spring.configLocations(this.xml("WithXorCsrfTokenRequestAttributeHandler"), this.xml("shared-controllers"))
.autowire();
// @formatter:off
MvcResult mvcResult = this.mvc.perform(get("/ok"))
.andExpect(status().isOk())
.andReturn();
MockHttpSession session = (MockHttpSession) mvcResult.getRequest().getSession();
MockHttpServletRequestBuilder ok = post("/ok")
.with(csrf())
.session(session);
this.mvc.perform(ok).andExpect(status().isForbidden());
// @formatter:on
}
@Test
public void postWhenHasCsrfTokenButSessionExpiresThenRequestIsCancelledAfterSuccessfulAuthentication()
throws Exception {