1
0
mirror of synced 2026-08-04 09:17:02 +00:00

Add CsrfTokenRepository (#3805)

* Create LazyCsrfTokenRepository

Fixes gh-3790

* Add CookieCsrfTokenRepository

Fixes gh-3009
This commit is contained in:
Rob Winch
2016-04-12 16:26:53 -05:00
committed by Joe Grandja
parent e9cb92bb74
commit d3a9cc6eae
14 changed files with 1461 additions and 857 deletions
@@ -33,6 +33,7 @@ import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfLogoutHandler;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.csrf.LazyCsrfTokenRepository;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
import org.springframework.security.web.session.InvalidSessionStrategy;
@@ -43,8 +44,9 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
/**
* Adds <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)"
* >CSRF</a> protection for the methods as specified by
* Adds
* <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)" >CSRF</a>
* protection for the methods as specified by
* {@link #requireCsrfProtectionMatcher(RequestMatcher)}.
*
* <h2>Security Filters</h2>
@@ -62,18 +64,18 @@ import org.springframework.util.Assert;
* <h2>Shared Objects Used</h2>
*
* <ul>
* <li>
* {@link ExceptionHandlingConfigurer#accessDeniedHandler(AccessDeniedHandler)} is used to
* determine how to handle CSRF attempts</li>
* <li>{@link ExceptionHandlingConfigurer#accessDeniedHandler(AccessDeniedHandler)} is
* used to determine how to handle CSRF attempts</li>
* <li>{@link InvalidSessionStrategy}</li>
* </ul>
*
* @author Rob Winch
* @since 3.2
*/
public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
private CsrfTokenRepository csrfTokenRepository = new HttpSessionCsrfTokenRepository();
public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
extends AbstractHttpConfigurer<CsrfConfigurer<H>, H> {
private CsrfTokenRepository csrfTokenRepository = new LazyCsrfTokenRepository(
new HttpSessionCsrfTokenRepository());
private RequestMatcher requireCsrfProtectionMatcher = CsrfFilter.DEFAULT_CSRF_MATCHER;
private List<RequestMatcher> ignoredCsrfProtectionMatchers = new ArrayList<RequestMatcher>();
@@ -86,12 +88,13 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends
/**
* Specify the {@link CsrfTokenRepository} to use. The default is an
* {@link HttpSessionCsrfTokenRepository}.
* {@link HttpSessionCsrfTokenRepository} wrapped by {@link LazyCsrfTokenRepository}.
*
* @param csrfTokenRepository the {@link CsrfTokenRepository} to use
* @return the {@link CsrfConfigurer} for further customizations
*/
public CsrfConfigurer<H> csrfTokenRepository(CsrfTokenRepository csrfTokenRepository) {
public CsrfConfigurer<H> csrfTokenRepository(
CsrfTokenRepository csrfTokenRepository) {
Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
this.csrfTokenRepository = csrfTokenRepository;
return this;
@@ -144,7 +147,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends
@SuppressWarnings("unchecked")
@Override
public void configure(H http) throws Exception {
CsrfFilter filter = new CsrfFilter(csrfTokenRepository);
CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
if (requireCsrfProtectionMatcher != null) {
filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
@@ -155,14 +158,14 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends
}
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
if (logoutConfigurer != null) {
logoutConfigurer.addLogoutHandler(new CsrfLogoutHandler(csrfTokenRepository));
logoutConfigurer
.addLogoutHandler(new CsrfLogoutHandler(this.csrfTokenRepository));
}
SessionManagementConfigurer<H> sessionConfigurer = http
.getConfigurer(SessionManagementConfigurer.class);
if (sessionConfigurer != null) {
sessionConfigurer
.addSessionAuthenticationStrategy(new CsrfAuthenticationStrategy(
csrfTokenRepository));
sessionConfigurer.addSessionAuthenticationStrategy(
new CsrfAuthenticationStrategy(this.csrfTokenRepository));
}
filter = postProcess(filter);
http.addFilter(filter);
@@ -175,12 +178,12 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends
* @return the {@link RequestMatcher} to use
*/
private RequestMatcher getRequireCsrfProtectionMatcher() {
if (ignoredCsrfProtectionMatchers.isEmpty()) {
return requireCsrfProtectionMatcher;
if (this.ignoredCsrfProtectionMatchers.isEmpty()) {
return this.requireCsrfProtectionMatcher;
}
return new AndRequestMatcher(requireCsrfProtectionMatcher,
new NegatedRequestMatcher(new OrRequestMatcher(
ignoredCsrfProtectionMatchers)));
return new AndRequestMatcher(this.requireCsrfProtectionMatcher,
new NegatedRequestMatcher(
new OrRequestMatcher(this.ignoredCsrfProtectionMatchers)));
}
/**
@@ -238,7 +241,8 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends
*/
private AccessDeniedHandler createAccessDeniedHandler(H http) {
InvalidSessionStrategy invalidSessionStrategy = getInvalidSessionStrategy(http);
AccessDeniedHandler defaultAccessDeniedHandler = getDefaultAccessDeniedHandler(http);
AccessDeniedHandler defaultAccessDeniedHandler = getDefaultAccessDeniedHandler(
http);
if (invalidSessionStrategy == null) {
return defaultAccessDeniedHandler;
}
@@ -258,16 +262,17 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>> extends
* @author Rob Winch
* @since 4.0
*/
private class IgnoreCsrfProtectionRegistry extends
AbstractRequestMatcherRegistry<IgnoreCsrfProtectionRegistry> {
private class IgnoreCsrfProtectionRegistry
extends AbstractRequestMatcherRegistry<IgnoreCsrfProtectionRegistry> {
public CsrfConfigurer<H> and() {
return CsrfConfigurer.this;
}
@Override
protected IgnoreCsrfProtectionRegistry chainRequestMatchers(
List<RequestMatcher> requestMatchers) {
ignoredCsrfProtectionMatchers.addAll(requestMatchers);
CsrfConfigurer.this.ignoredCsrfProtectionMatchers.addAll(requestMatchers);
return this;
}
}
@@ -15,6 +15,8 @@
*/
package org.springframework.security.config.http;
import org.w3c.dom.Element;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
@@ -31,13 +33,13 @@ import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfLogoutHandler;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.csrf.LazyCsrfTokenRepository;
import org.springframework.security.web.csrf.MissingCsrfTokenException;
import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor;
import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Parser for the {@code CsrfFilter}.
@@ -55,6 +57,7 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
private String csrfRepositoryRef;
private BeanDefinition csrfFilter;
@Override
public BeanDefinition parse(Element element, ParserContext pc) {
boolean disabled = element != null
&& "true".equals(element.getAttribute("disabled"));
@@ -73,29 +76,33 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
String matcherRef = null;
if (element != null) {
csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);
this.csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);
matcherRef = element.getAttribute(ATT_MATCHER);
}
if (!StringUtils.hasText(csrfRepositoryRef)) {
if (!StringUtils.hasText(this.csrfRepositoryRef)) {
RootBeanDefinition csrfTokenRepository = new RootBeanDefinition(
HttpSessionCsrfTokenRepository.class);
csrfRepositoryRef = pc.getReaderContext().generateBeanName(
csrfTokenRepository);
pc.registerBeanComponent(new BeanComponentDefinition(csrfTokenRepository,
csrfRepositoryRef));
BeanDefinitionBuilder lazyTokenRepository = BeanDefinitionBuilder
.rootBeanDefinition(LazyCsrfTokenRepository.class);
lazyTokenRepository.addConstructorArgValue(csrfTokenRepository);
this.csrfRepositoryRef = pc.getReaderContext()
.generateBeanName(lazyTokenRepository.getBeanDefinition());
pc.registerBeanComponent(new BeanComponentDefinition(
lazyTokenRepository.getBeanDefinition(), this.csrfRepositoryRef));
}
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.rootBeanDefinition(CsrfFilter.class);
builder.addConstructorArgReference(csrfRepositoryRef);
builder.addConstructorArgReference(this.csrfRepositoryRef);
if (StringUtils.hasText(matcherRef)) {
builder.addPropertyReference("requireCsrfProtectionMatcher", matcherRef);
}
csrfFilter = builder.getBeanDefinition();
return csrfFilter;
this.csrfFilter = builder.getBeanDefinition();
return this.csrfFilter;
}
/**
@@ -108,7 +115,7 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
BeanMetadataElement defaultDeniedHandler) {
BeanMetadataElement accessDeniedHandler = createAccessDeniedHandler(
invalidSessionStrategy, defaultDeniedHandler);
csrfFilter.getPropertyValues().addPropertyValue("accessDeniedHandler",
this.csrfFilter.getPropertyValues().addPropertyValue("accessDeniedHandler",
accessDeniedHandler);
}
@@ -152,14 +159,14 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
BeanDefinition getCsrfAuthenticationStrategy() {
BeanDefinitionBuilder csrfAuthenticationStrategy = BeanDefinitionBuilder
.rootBeanDefinition(CsrfAuthenticationStrategy.class);
csrfAuthenticationStrategy.addConstructorArgReference(csrfRepositoryRef);
csrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef);
return csrfAuthenticationStrategy.getBeanDefinition();
}
BeanDefinition getCsrfLogoutHandler() {
BeanDefinitionBuilder csrfAuthenticationStrategy = BeanDefinitionBuilder
.rootBeanDefinition(CsrfLogoutHandler.class);
csrfAuthenticationStrategy.addConstructorArgReference(csrfRepositoryRef);
csrfAuthenticationStrategy.addConstructorArgReference(this.csrfRepositoryRef);
return csrfAuthenticationStrategy.getBeanDefinition();
}
}
@@ -15,11 +15,10 @@
*/
package org.springframework.security.config.annotation.web.configurers
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
import javax.servlet.http.HttpServletResponse
import org.springframework.context.annotation.Configuration
import spock.lang.Unroll
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.security.config.annotation.BaseSpringSpec
@@ -27,15 +26,13 @@ import org.springframework.security.config.annotation.authentication.builders.Au
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.web.access.AccessDeniedHandler
import org.springframework.security.web.csrf.CsrfFilter
import org.springframework.security.web.csrf.CsrfTokenRepository
import org.springframework.security.web.util.matcher.AntPathRequestMatcher
import org.springframework.security.web.util.matcher.RequestMatcher
import org.springframework.web.servlet.support.RequestDataValueProcessor
import spock.lang.Unroll
/**
*
* @author Rob Winch
@@ -45,31 +42,31 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Unroll
def "csrf applied by default"() {
setup:
loadConfig(CsrfAppliedDefaultConfig)
request.method = httpMethod
clearCsrfToken()
loadConfig(CsrfAppliedDefaultConfig)
request.method = httpMethod
clearCsrfToken()
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == httpStatus
response.status == httpStatus
where:
httpMethod | httpStatus
'POST' | HttpServletResponse.SC_FORBIDDEN
'PUT' | HttpServletResponse.SC_FORBIDDEN
'PATCH' | HttpServletResponse.SC_FORBIDDEN
'DELETE' | HttpServletResponse.SC_FORBIDDEN
'INVALID' | HttpServletResponse.SC_FORBIDDEN
'GET' | HttpServletResponse.SC_OK
'HEAD' | HttpServletResponse.SC_OK
'TRACE' | HttpServletResponse.SC_OK
'OPTIONS' | HttpServletResponse.SC_OK
httpMethod | httpStatus
'POST' | HttpServletResponse.SC_FORBIDDEN
'PUT' | HttpServletResponse.SC_FORBIDDEN
'PATCH' | HttpServletResponse.SC_FORBIDDEN
'DELETE' | HttpServletResponse.SC_FORBIDDEN
'INVALID' | HttpServletResponse.SC_FORBIDDEN
'GET' | HttpServletResponse.SC_OK
'HEAD' | HttpServletResponse.SC_OK
'TRACE' | HttpServletResponse.SC_OK
'OPTIONS' | HttpServletResponse.SC_OK
}
def "csrf default creates CsrfRequestDataValueProcessor"() {
when:
loadConfig(CsrfAppliedDefaultConfig)
loadConfig(CsrfAppliedDefaultConfig)
then:
context.getBean(RequestDataValueProcessor)
context.getBean(RequestDataValueProcessor)
}
@EnableWebSecurity
@@ -82,14 +79,14 @@ class CsrfConfigurerTests extends BaseSpringSpec {
def "csrf disable"() {
setup:
loadConfig(DisableCsrfConfig)
request.method = "POST"
clearCsrfToken()
loadConfig(DisableCsrfConfig)
request.method = "POST"
clearCsrfToken()
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
!findFilter(CsrfFilter)
response.status == HttpServletResponse.SC_OK
!findFilter(CsrfFilter)
response.status == HttpServletResponse.SC_OK
}
@EnableWebSecurity
@@ -98,29 +95,29 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.csrf().disable()
}
}
def "SEC-2498: Disable CSRF enables RequestCache for any method"() {
setup:
loadConfig(DisableCsrfEnablesRequestCacheConfig)
request.requestURI = '/tosave'
request.method = "POST"
clearCsrfToken()
loadConfig(DisableCsrfEnablesRequestCacheConfig)
request.requestURI = '/tosave'
request.method = "POST"
clearCsrfToken()
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.redirectedUrl
response.redirectedUrl
when:
super.setupWeb(request.session)
request.method = "POST"
request.servletPath = '/login'
request.parameters['username'] = ['user'] as String[]
request.parameters['password'] = ['password'] as String[]
springSecurityFilterChain.doFilter(request,response,chain)
super.setupWeb(request.session)
request.method = "POST"
request.servletPath = '/login'
request.parameters['username'] = ['user'] as String[]
request.parameters['password'] = ['password'] as String[]
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.redirectedUrl == 'http://localhost/tosave'
response.redirectedUrl == 'http://localhost/tosave'
}
@EnableWebSecurity
@@ -129,38 +126,37 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().and()
.csrf().disable()
.formLogin().and()
.csrf().disable()
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
}
def "SEC-2422: csrf expire CSRF token and session-management invalid-session-url"() {
setup:
loadConfig(InvalidSessionUrlConfig)
request.session.clearAttributes()
request.setParameter("_csrf","abc")
request.method = "POST"
loadConfig(InvalidSessionUrlConfig)
request.session.clearAttributes()
request.setParameter("_csrf","abc")
request.method = "POST"
when: "No existing expected CsrfToken (session times out) and a POST"
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the session timeout page page"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/error/sessionError"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/error/sessionError"
when: "Existing expected CsrfToken and a POST (invalid token provided)"
response = new MockHttpServletResponse()
request = new MockHttpServletRequest(session: request.session, method:'POST')
springSecurityFilterChain.doFilter(request,response,chain)
response = new MockHttpServletResponse()
request = new MockHttpServletRequest(session: request.session, method:'POST')
springSecurityFilterChain.doFilter(request,response,chain)
then: "Access Denied occurs"
response.status == HttpServletResponse.SC_FORBIDDEN
response.status == HttpServletResponse.SC_FORBIDDEN
}
@EnableWebSecurity
@@ -168,26 +164,26 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().and()
.sessionManagement()
.csrf().and()
.sessionManagement()
.invalidSessionUrl("/error/sessionError")
}
}
def "csrf requireCsrfProtectionMatcher"() {
setup:
RequireCsrfProtectionMatcherConfig.matcher = Mock(RequestMatcher)
RequireCsrfProtectionMatcherConfig.matcher.matches(_) >>> [false,true]
loadConfig(RequireCsrfProtectionMatcherConfig)
clearCsrfToken()
RequireCsrfProtectionMatcherConfig.matcher = Mock(RequestMatcher)
RequireCsrfProtectionMatcherConfig.matcher.matches(_) >>> [false, true]
loadConfig(RequireCsrfProtectionMatcherConfig)
clearCsrfToken()
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_OK
response.status == HttpServletResponse.SC_OK
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_FORBIDDEN
response.status == HttpServletResponse.SC_FORBIDDEN
}
@EnableWebSecurity
@@ -197,53 +193,53 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.csrf()
.requireCsrfProtectionMatcher(matcher)
}
}
def "csrf csrfTokenRepository"() {
setup:
CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
loadConfig(CsrfTokenRepositoryConfig)
clearCsrfToken()
CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
loadConfig(CsrfTokenRepositoryConfig)
clearCsrfToken()
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
1 * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_OK
1 * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_OK
}
def "csrf clears on logout"() {
setup:
CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
1 * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
loadConfig(CsrfTokenRepositoryConfig)
login()
request.method = "POST"
request.servletPath = "/logout"
CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
1 * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
loadConfig(CsrfTokenRepositoryConfig)
login()
request.method = "POST"
request.servletPath = "/logout"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
1 * CsrfTokenRepositoryConfig.repo.saveToken(null, _, _)
1 * CsrfTokenRepositoryConfig.repo.saveToken(null, _, _)
}
def "csrf clears on login"() {
setup:
CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
(1.._) * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
(1.._) * CsrfTokenRepositoryConfig.repo.generateToken(_) >> csrfToken
loadConfig(CsrfTokenRepositoryConfig)
request.method = "POST"
request.getSession()
request.servletPath = "/login"
request.setParameter("username", "user")
request.setParameter("password", "password")
CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
(1.._) * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
(1.._) * CsrfTokenRepositoryConfig.repo.generateToken(_) >> csrfToken
loadConfig(CsrfTokenRepositoryConfig)
request.method = "POST"
request.getSession()
request.servletPath = "/login"
request.setParameter("username", "user")
request.setParameter("password", "password")
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.redirectedUrl == "/"
(1.._) * CsrfTokenRepositoryConfig.repo.saveToken(null, _, _)
response.redirectedUrl == "/"
(1.._) * CsrfTokenRepositoryConfig.repo.saveToken(null, _, _)
}
@EnableWebSecurity
@@ -253,30 +249,30 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.formLogin()
.and()
.csrf()
.csrf()
.csrfTokenRepository(repo)
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
}
def "csrf access denied handler"() {
setup:
AccessDeniedHandlerConfig.deniedHandler = Mock(AccessDeniedHandler)
1 * AccessDeniedHandlerConfig.deniedHandler.handle(_, _, _)
loadConfig(AccessDeniedHandlerConfig)
clearCsrfToken()
request.method = "POST"
AccessDeniedHandlerConfig.deniedHandler = Mock(AccessDeniedHandler)
1 * AccessDeniedHandlerConfig.deniedHandler.handle(_, _, _)
loadConfig(AccessDeniedHandlerConfig)
clearCsrfToken()
request.method = "POST"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_OK
response.status == HttpServletResponse.SC_OK
}
@EnableWebSecurity
@@ -286,24 +282,24 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.exceptionHandling()
.accessDeniedHandler(deniedHandler)
}
}
def "formLogin requires CSRF token"() {
setup:
loadConfig(FormLoginConfig)
clearCsrfToken()
request.setParameter("username", "user")
request.setParameter("password", "password")
request.servletPath = "/login"
request.method = "POST"
loadConfig(FormLoginConfig)
clearCsrfToken()
request.setParameter("username", "user")
request.setParameter("password", "password")
request.servletPath = "/login"
request.method = "POST"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_FORBIDDEN
currentAuthentication == null
response.status == HttpServletResponse.SC_FORBIDDEN
currentAuthentication == null
}
@EnableWebSecurity
@@ -313,34 +309,34 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.formLogin()
}
}
def "logout requires CSRF token"() {
setup:
loadConfig(LogoutConfig)
clearCsrfToken()
login()
request.servletPath = "/logout"
request.method = "POST"
loadConfig(LogoutConfig)
clearCsrfToken()
login()
request.servletPath = "/logout"
request.method = "POST"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then: "logout is not allowed and user is still authenticated"
response.status == HttpServletResponse.SC_FORBIDDEN
currentAuthentication != null
response.status == HttpServletResponse.SC_FORBIDDEN
currentAuthentication != null
}
def "SEC-2543: CSRF means logout requires POST"() {
setup:
loadConfig(LogoutConfig)
login()
request.servletPath = "/logout"
request.method = "GET"
loadConfig(LogoutConfig)
login()
request.servletPath = "/logout"
request.method = "GET"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then: "logout with GET is not performed"
currentAuthentication != null
currentAuthentication != null
}
@EnableWebSecurity
@@ -350,20 +346,20 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.formLogin()
}
}
def "CSRF can explicitly enable GET for logout"() {
setup:
loadConfig(LogoutAllowsGetConfig)
login()
request.servletPath = "/logout"
request.method = "GET"
loadConfig(LogoutAllowsGetConfig)
login()
request.servletPath = "/logout"
request.method = "GET"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then: "logout with GET is not performed"
currentAuthentication == null
currentAuthentication == null
}
@EnableWebSecurity
@@ -373,64 +369,64 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().and()
.logout()
.formLogin().and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
}
}
def "csrf disables POST requests from RequestCache"() {
setup:
CsrfDisablesPostRequestFromRequestCacheConfig.repo = Mock(CsrfTokenRepository)
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.generateToken(_) >> csrfToken
loadConfig(CsrfDisablesPostRequestFromRequestCacheConfig)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
request.method = "POST"
CsrfDisablesPostRequestFromRequestCacheConfig.repo = Mock(CsrfTokenRepository)
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.generateToken(_) >> csrfToken
loadConfig(CsrfDisablesPostRequestFromRequestCacheConfig)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
request.method = "POST"
when: "CSRF passes and our session times out"
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the login page"
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
when: "authenticate successfully"
super.setupWeb(request.session)
request.servletPath = "/login"
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
super.setupWeb(request.session)
request.servletPath = "/login"
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to default success because we don't want csrf attempts made prior to authentication to pass"
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
}
def "csrf enables GET requests with RequestCache"() {
setup:
CsrfDisablesPostRequestFromRequestCacheConfig.repo = Mock(CsrfTokenRepository)
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.generateToken(_) >> csrfToken
loadConfig(CsrfDisablesPostRequestFromRequestCacheConfig)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
request.method = "GET"
CsrfDisablesPostRequestFromRequestCacheConfig.repo = Mock(CsrfTokenRepository)
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.generateToken(_) >> csrfToken
loadConfig(CsrfDisablesPostRequestFromRequestCacheConfig)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
request.method = "GET"
when: "CSRF passes and our session times out"
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the login page"
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
when: "authenticate successfully"
super.setupWeb(request.session)
request.servletPath = "/login"
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
super.setupWeb(request.session)
request.servletPath = "/login"
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to original URL since it was a GET"
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/some-url"
(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/some-url"
}
@EnableWebSecurity
@@ -440,18 +436,18 @@ class CsrfConfigurerTests extends BaseSpringSpec {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.formLogin()
.and()
.csrf()
.csrf()
.csrfTokenRepository(repo)
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
}
@@ -463,6 +459,39 @@ class CsrfConfigurerTests extends BaseSpringSpec {
thrown(IllegalArgumentException)
}
def 'default does not create session'() {
setup:
request = new MockHttpServletRequest(method:'GET')
loadConfig(DefaultDoesNotCreateSession)
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
request.getSession(false) == null
}
@EnableWebSecurity(debug=true)
static class DefaultDoesNotCreateSession extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests()
.anyRequest().permitAll()
.and()
.formLogin().and()
.httpBasic();
// @formatter:on
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER")
}
}
def clearCsrfToken() {
request.removeAllParameters()
}
@@ -12,12 +12,11 @@
*/
package org.springframework.security.config.http
import static org.mockito.Matchers.*
import static org.mockito.Mockito.*
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import spock.lang.Unroll
import org.springframework.mock.web.MockFilterChain
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
@@ -36,7 +35,8 @@ import org.springframework.security.web.csrf.DefaultCsrfToken
import org.springframework.security.web.util.matcher.RequestMatcher
import org.springframework.web.servlet.support.RequestDataValueProcessor
import spock.lang.Unroll
import static org.mockito.Matchers.*
import static org.mockito.Mockito.*
/**
*
@@ -73,256 +73,253 @@ class CsrfConfigTests extends AbstractHttpConfigTests {
def 'csrf disabled'() {
when:
httpAutoConfig {
csrf(disabled:true)
}
createAppContext()
httpAutoConfig { csrf(disabled:true) }
createAppContext()
then:
!getFilter(CsrfFilter)
!getFilter(CsrfFilter)
}
@Unroll
def 'csrf defaults'() {
setup:
httpAutoConfig {
'csrf'()
}
createAppContext()
httpAutoConfig { 'csrf'() }
createAppContext()
when:
request.method = httpMethod
springSecurityFilterChain.doFilter(request,response,chain)
request.method = httpMethod
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == httpStatus
response.status == httpStatus
where:
httpMethod | httpStatus
'POST' | HttpServletResponse.SC_FORBIDDEN
'PUT' | HttpServletResponse.SC_FORBIDDEN
'PATCH' | HttpServletResponse.SC_FORBIDDEN
'DELETE' | HttpServletResponse.SC_FORBIDDEN
'INVALID' | HttpServletResponse.SC_FORBIDDEN
'GET' | HttpServletResponse.SC_OK
'HEAD' | HttpServletResponse.SC_OK
'TRACE' | HttpServletResponse.SC_OK
'OPTIONS' | HttpServletResponse.SC_OK
httpMethod | httpStatus
'POST' | HttpServletResponse.SC_FORBIDDEN
'PUT' | HttpServletResponse.SC_FORBIDDEN
'PATCH' | HttpServletResponse.SC_FORBIDDEN
'DELETE' | HttpServletResponse.SC_FORBIDDEN
'INVALID' | HttpServletResponse.SC_FORBIDDEN
'GET' | HttpServletResponse.SC_OK
'HEAD' | HttpServletResponse.SC_OK
'TRACE' | HttpServletResponse.SC_OK
'OPTIONS' | HttpServletResponse.SC_OK
}
def 'csrf default creates CsrfRequestDataValueProcessor'() {
when:
httpAutoConfig {
'csrf'()
}
createAppContext()
httpAutoConfig { 'csrf'() }
createAppContext()
then:
appContext.getBean("requestDataValueProcessor",RequestDataValueProcessor)
appContext.getBean("requestDataValueProcessor",RequestDataValueProcessor)
}
def 'csrf custom AccessDeniedHandler'() {
setup:
httpAutoConfig {
'access-denied-handler'(ref:'adh')
'csrf'()
}
mockBean(AccessDeniedHandler,'adh')
createAppContext()
AccessDeniedHandler adh = appContext.getBean(AccessDeniedHandler)
request.method = "POST"
httpAutoConfig {
'access-denied-handler'(ref:'adh')
'csrf'()
}
mockBean(AccessDeniedHandler,'adh')
createAppContext()
AccessDeniedHandler adh = appContext.getBean(AccessDeniedHandler)
request.method = "POST"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
verify(adh).handle(any(HttpServletRequest),any(HttpServletResponse),any(AccessDeniedException))
response.status == HttpServletResponse.SC_OK // our mock doesn't do anything
verify(adh).handle(any(HttpServletRequest),any(HttpServletResponse),any(AccessDeniedException))
response.status == HttpServletResponse.SC_OK // our mock doesn't do anything
}
def "csrf disables posts for RequestCache"() {
setup:
httpAutoConfig {
'csrf'('token-repository-ref':'repo')
'intercept-url'(pattern:"/**",access:'ROLE_USER')
}
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
request.method = "POST"
httpAutoConfig {
'csrf'('token-repository-ref':'repo')
'intercept-url'(pattern:"/**",access:'ROLE_USER')
}
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
request.method = "POST"
when: "CSRF passes and our session times out"
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the login page"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
when: "authenticate successfully"
response = new MockHttpServletResponse()
request = new MockHttpServletRequest(session: request.session)
request.servletPath = "/login"
request.setParameter(token.parameterName,token.token)
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
response = new MockHttpServletResponse()
request = new MockHttpServletRequest(session: request.session)
request.servletPath = "/login"
request.setParameter(token.parameterName,token.token)
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to default success because we don't want csrf attempts made prior to authentication to pass"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/"
}
def "csrf enables gets for RequestCache"() {
setup:
httpAutoConfig {
'csrf'('token-repository-ref':'repo')
'intercept-url'(pattern:"/**",access:'ROLE_USER')
}
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
httpAutoConfig {
'csrf'('token-repository-ref':'repo')
'intercept-url'(pattern:"/**",access:'ROLE_USER')
}
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
request.method = "GET"
request.setParameter(token.parameterName,token.token)
request.servletPath = "/some-url"
request.requestURI = "/some-url"
request.method = "GET"
when: "CSRF passes and our session times out"
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the login page"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/login"
when: "authenticate successfully"
response = new MockHttpServletResponse()
request = new MockHttpServletRequest(session: request.session)
request.servletPath = "/login"
request.setParameter(token.parameterName,token.token)
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
response = new MockHttpServletResponse()
request = new MockHttpServletRequest(session: request.session)
request.servletPath = "/login"
request.setParameter(token.parameterName,token.token)
request.setParameter("username","user")
request.setParameter("password","password")
request.method = "POST"
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to original URL since it was a GET"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/some-url"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "http://localhost/some-url"
}
def "SEC-2422: csrf expire CSRF token and session-management invalid-session-url"() {
setup:
httpAutoConfig {
'csrf'()
'session-management'('invalid-session-url': '/error/sessionError')
}
createAppContext()
request.setParameter("_csrf","abc")
request.method = "POST"
httpAutoConfig {
'csrf'()
'session-management'('invalid-session-url': '/error/sessionError')
}
createAppContext()
request.setParameter("_csrf","abc")
request.method = "POST"
when: "No existing expected CsrfToken (session times out) and a POST"
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then: "sent to the session timeout page page"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/error/sessionError"
response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
response.redirectedUrl == "/error/sessionError"
when: "Existing expected CsrfToken and a POST (invalid token provided)"
response = new MockHttpServletResponse()
request = new MockHttpServletRequest(session: request.session, method:'POST')
springSecurityFilterChain.doFilter(request,response,chain)
response = new MockHttpServletResponse()
request = new MockHttpServletRequest(session: request.session, method:'POST')
springSecurityFilterChain.doFilter(request,response,chain)
then: "Access Denied occurs"
response.status == HttpServletResponse.SC_FORBIDDEN
response.status == HttpServletResponse.SC_FORBIDDEN
}
def "csrf requireCsrfProtectionMatcher"() {
setup:
httpAutoConfig {
'csrf'('request-matcher-ref':'matcher')
}
mockBean(RequestMatcher,'matcher')
createAppContext()
request.method = 'POST'
RequestMatcher matcher = appContext.getBean("matcher",RequestMatcher)
httpAutoConfig { 'csrf'('request-matcher-ref':'matcher') }
mockBean(RequestMatcher,'matcher')
createAppContext()
request.method = 'POST'
RequestMatcher matcher = appContext.getBean("matcher",RequestMatcher)
when:
when(matcher.matches(any(HttpServletRequest))).thenReturn(false)
springSecurityFilterChain.doFilter(request,response,chain)
when(matcher.matches(any(HttpServletRequest))).thenReturn(false)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_OK
response.status == HttpServletResponse.SC_OK
when:
when(matcher.matches(any(HttpServletRequest))).thenReturn(true)
springSecurityFilterChain.doFilter(request,response,chain)
when(matcher.matches(any(HttpServletRequest))).thenReturn(true)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_FORBIDDEN
response.status == HttpServletResponse.SC_FORBIDDEN
}
def "csrf csrfTokenRepository default delays save"() {
setup:
httpAutoConfig {
}
createAppContext()
request.method = "GET"
when:
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_OK
request.getSession(false) == null
}
def "csrf csrfTokenRepository"() {
setup:
httpAutoConfig {
'csrf'('token-repository-ref':'repo')
}
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.method = "POST"
httpAutoConfig { 'csrf'('token-repository-ref':'repo') }
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.method = "POST"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_OK
response.status == HttpServletResponse.SC_OK
when:
request.setParameter(token.parameterName,token.token+"INVALID")
springSecurityFilterChain.doFilter(request,response,chain)
request.setParameter(token.parameterName,token.token+"INVALID")
springSecurityFilterChain.doFilter(request,response,chain)
then:
response.status == HttpServletResponse.SC_FORBIDDEN
response.status == HttpServletResponse.SC_FORBIDDEN
}
def "csrf clears on login"() {
setup:
httpAutoConfig {
'csrf'('token-repository-ref':'repo')
}
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.method = "POST"
request.setParameter("username","user")
request.setParameter("password","password")
request.servletPath = "/login"
httpAutoConfig { 'csrf'('token-repository-ref':'repo') }
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
when(repo.generateToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.method = "POST"
request.setParameter("username","user")
request.setParameter("password","password")
request.servletPath = "/login"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
verify(repo, atLeastOnce()).saveToken(eq(null),any(HttpServletRequest), any(HttpServletResponse))
verify(repo, atLeastOnce()).saveToken(eq(null),any(HttpServletRequest), any(HttpServletResponse))
}
def "csrf clears on logout"() {
setup:
httpAutoConfig {
'csrf'('token-repository-ref':'repo')
}
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.method = "POST"
request.servletPath = "/logout"
httpAutoConfig { 'csrf'('token-repository-ref':'repo') }
mockBean(CsrfTokenRepository,'repo')
createAppContext()
CsrfTokenRepository repo = appContext.getBean("repo",CsrfTokenRepository)
CsrfToken token = new DefaultCsrfToken("X-CSRF-TOKEN","_csrf", "abc")
when(repo.loadToken(any(HttpServletRequest))).thenReturn(token)
request.setParameter(token.parameterName,token.token)
request.method = "POST"
request.servletPath = "/logout"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
verify(repo).saveToken(eq(null),any(HttpServletRequest), any(HttpServletResponse))
verify(repo).saveToken(eq(null),any(HttpServletRequest), any(HttpServletResponse))
}
def "SEC-2495: csrf disables logout on GET"() {
setup:
httpAutoConfig {
'csrf'()
}
createAppContext()
login()
request.method = "GET"
request.requestURI = "/logout"
httpAutoConfig { 'csrf'() }
createAppContext()
login()
request.method = "GET"
request.requestURI = "/logout"
when:
springSecurityFilterChain.doFilter(request,response,chain)
springSecurityFilterChain.doFilter(request,response,chain)
then:
getAuthentication(request) != null
getAuthentication(request) != null
}