From fc93ca4d0b1f1f945284581c3fedf7a4f56409c9 Mon Sep 17 00:00:00 2001 From: Ricardo Campos Date: Sat, 5 Oct 2024 19:45:55 -0300 Subject: [PATCH] fix: csrf token filter handler - attemp #2 --- .../server/config/SecurityConfig.java | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/br/com/tasknoteapp/server/config/SecurityConfig.java b/server/src/main/java/br/com/tasknoteapp/server/config/SecurityConfig.java index b8f3cb6..29355c1 100644 --- a/server/src/main/java/br/com/tasknoteapp/server/config/SecurityConfig.java +++ b/server/src/main/java/br/com/tasknoteapp/server/config/SecurityConfig.java @@ -9,6 +9,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import java.io.IOException; +import java.util.function.Supplier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -30,6 +31,9 @@ import org.springframework.security.web.authentication.www.BasicAuthenticationFi import org.springframework.security.web.csrf.CookieCsrfTokenRepository; import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; +import org.springframework.security.web.csrf.CsrfTokenRequestHandler; +import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler; +import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; /** This class contains security configurations. */ @@ -57,7 +61,8 @@ public class SecurityConfig { custom .ignoringRequestMatchers("/auth/**") .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) - .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())) + .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler())) + .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class) .authorizeHttpRequests( request -> request @@ -75,8 +80,6 @@ public class SecurityConfig { http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - http.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); - return http.build(); } @@ -100,6 +103,39 @@ public class SecurityConfig { } } +final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler { + private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler(); + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, Supplier csrfToken) { + /* + * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of + * the CsrfToken when it is rendered in the response body. + */ + this.delegate.handle(request, response, csrfToken); + } + + @Override + public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) { + /* + * If the request contains a request header, use CsrfTokenRequestAttributeHandler + * to resolve the CsrfToken. This applies when a single-page application includes + * the header value automatically, which was obtained via a cookie containing the + * raw CsrfToken. + */ + if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) { + return super.resolveCsrfTokenValue(request, csrfToken); + } + /* + * In all other cases (e.g. if the request contains a request parameter), use + * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies + * when a server-side rendered form includes the _csrf request parameter as a + * hidden input. + */ + return this.delegate.resolveCsrfTokenValue(request, csrfToken); + } +} + final class CsrfCookieFilter extends OncePerRequestFilter { @Override