Skip to content

Commit b7dc65d

Browse files
holgerhagenarolfes
authored andcommitted
Closes #2510 - fixed csrf handling without disabling it
1 parent 5582318 commit b7dc65d

File tree

3 files changed

+74
-1
lines changed

3 files changed

+74
-1
lines changed

rest/taskana-rest-spring-example-boot/src/main/java/pro/taskana/example/boot/security/BootWebSecurityConfigurer.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
7575
if (enableCsrf) {
7676
CookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
7777
csrfTokenRepository.setCookiePath("/");
78-
http.csrf(csrf -> csrf.csrfTokenRepository(csrfTokenRepository));
78+
http.csrf(
79+
csrf ->
80+
csrf.csrfTokenRepository(csrfTokenRepository)
81+
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()))
82+
.addFilterAfter(new CsrfCookieFilter(), SpringSecurityToJaasFilter.class);
7983
} else {
8084
http.csrf(AbstractHttpConfigurer::disable).httpBasic(Customizer.withDefaults());
8185
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package pro.taskana.example.boot.security;
2+
3+
import jakarta.servlet.FilterChain;
4+
import jakarta.servlet.ServletException;
5+
import jakarta.servlet.http.HttpServletRequest;
6+
import jakarta.servlet.http.HttpServletResponse;
7+
import java.io.IOException;
8+
import org.springframework.security.web.csrf.CsrfToken;
9+
import org.springframework.web.filter.OncePerRequestFilter;
10+
11+
final class CsrfCookieFilter extends OncePerRequestFilter {
12+
13+
@Override
14+
protected void doFilterInternal(
15+
HttpServletRequest request,
16+
@SuppressWarnings("NullableProblems") HttpServletResponse response,
17+
FilterChain filterChain)
18+
throws ServletException, IOException {
19+
CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
20+
// Render the token value to a cookie by causing the deferred token to be loaded
21+
csrfToken.getToken();
22+
23+
filterChain.doFilter(request, response);
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package pro.taskana.example.boot.security;
2+
3+
import jakarta.servlet.http.HttpServletRequest;
4+
import jakarta.servlet.http.HttpServletResponse;
5+
import java.util.function.Supplier;
6+
import org.springframework.security.web.csrf.CsrfToken;
7+
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
8+
import org.springframework.security.web.csrf.CsrfTokenRequestHandler;
9+
import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler;
10+
import org.springframework.util.StringUtils;
11+
12+
final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
13+
private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler();
14+
15+
@Override
16+
public void handle(
17+
HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
18+
/*
19+
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
20+
* the CsrfToken when it is rendered in the response body.
21+
*/
22+
this.delegate.handle(request, response, csrfToken);
23+
}
24+
25+
@Override
26+
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
27+
/*
28+
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
29+
* to resolve the CsrfToken. This applies when a single-page application includes
30+
* the header value automatically, which was obtained via a cookie containing the
31+
* raw CsrfToken.
32+
*/
33+
if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
34+
return super.resolveCsrfTokenValue(request, csrfToken);
35+
}
36+
/*
37+
* In all other cases (e.g. if the request contains a request parameter), use
38+
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
39+
* when a server-side rendered form includes the _csrf request parameter as a
40+
* hidden input.
41+
*/
42+
return this.delegate.resolveCsrfTokenValue(request, csrfToken);
43+
}
44+
}

0 commit comments

Comments
 (0)