Fix CSRF protection provided by @EnableWebSocketSecurity / Stomp
emopti-jrufer opened this issue ยท 31 comments
CSRF protection provided by @EnableWebSocketSecurity is broken. I have identified 2 things that prevent the CsrfChannelInterceptor from validating the CSRF token from the CONNECT headers.
First problem encounted was the stack trace below. The CsrfChannelInterceptor was trying to access the CsrfToken when processing the CONNECT message. The changes to made in 475b3bb and d94677f to defer the loading of the token caused the deferred supplier to try to access the http upgrade HttpServletRequest after it has gone out of scope.
Attempting to come up with a fix for the first problem I added an additional HandshakeInterceptor that forced eager loading the CsrfToken while they upgrade HttpServeletRequest was still in scope. I added the source below for clarity.
This overcame the first problem but then the CsrfChannelInterceptor started throwing InvalidCsrfTokenException. After investigating it became apparent the CsrfChannelInterceptor was only design to work with tokens that were not masked. Since XorCsrfTokenRequestAttributeHandler is now the default implementation for CsrfTokenRequestHandler the CsrfChannelInterceptor needs to be modified to have the same or similar logic as the CsrfFilter#doFilterInternal.
stack_trace: org.springframework.messaging.MessageDeliveryException: Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:149)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:125)
at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient(StompSubProtocolHandler.java:310)
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:335)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:113)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:84)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:81)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:415)
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:129)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:515)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:301)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:85)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:183)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:162)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:157)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:60)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.IllegalStateException: The request object has been recycled and is no longer associated with this facade
at org.apache.catalina.connector.RequestFacade.getAttribute(RequestFacade.java:279)
at jakarta.servlet.ServletRequestWrapper.getAttribute(ServletRequestWrapper.java:85)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.getCurrentSession(SessionRepositoryFilter.java:238)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.getSession(SessionRepositoryFilter.java:281)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.getSession(SessionRepositoryFilter.java:194)
at jakarta.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:244)
at jakarta.servlet.http.HttpServletRequestWrapper.getSession(HttpServletRequestWrapper.java:244)
at org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.loadToken(HttpSessionCsrfTokenRepository.java:65)
at org.springframework.security.web.csrf.RepositoryDeferredCsrfToken.init(RepositoryDeferredCsrfToken.java:63)
at org.springframework.security.web.csrf.RepositoryDeferredCsrfToken.get(RepositoryDeferredCsrfToken.java:48)
at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler.lambda$deferCsrfTokenUpdate$0(XorCsrfTokenRequestAttributeHandler.java:63)
at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler$CachedCsrfTokenSupplier.get(XorCsrfTokenRequestAttributeHandler.java:139)
at org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler$CachedCsrfTokenSupplier.get(XorCsrfTokenRequestAttributeHandler.java:126)
at org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler$SupplierCsrfToken.getDelegate(CsrfTokenRequestAttributeHandler.java:89)
at org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler$SupplierCsrfToken.getHeaderName(CsrfTokenRequestAttributeHandler.java:75)
at org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor.preSend(CsrfChannelInterceptor.java:56)
at org.springframework.messaging.support.AbstractMessageChannel$ChannelInterceptorChain.applyPreSend(AbstractMessageChannel.java:181)
at org.springframework.messaging.support.AbstractMessageChannel.send(AbstractMessageChannel.java:135)
... 30 common frames omitted
public class CsrfTokenHandshakeInterceptorFix implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
HttpServletRequest httpRequest = ((ServletServerHttpRequest) request).getServletRequest();
CsrfToken token = (CsrfToken) httpRequest.getAttribute(CsrfToken.class.getName());
if (token == null) {
return true;
}
// Eagerly resolve token values
token = new DefaultCsrfToken(token.getHeaderName(), token.getParameterName(), token.getToken());
attributes.put(CsrfToken.class.getName(), token);
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
}
})Hi @emopti-jrufer! Thanks for reporting this issue.
Would you be able to put together a minimal, reproducible sample with a unit test that demonstrates the behavior? Ideally any external dependencies (if any) would be mocked, but I'm not personally familiar with how to do so. If it's not possible to put together a sample, it may be a bit of time before I could take a look at this.
@sjohnr I created a sample that reproduces the issues. https://github.com/emopti-jrufer/spring-security-issue-12378-sample.
I faced the exact same Issue. AbstractSecurityWebSocketMessageBrokerConfigurer is deprecated so I went with @EnableWebSocketSecurity. I also have a custom ChannelInterceptor in place which checks the JWT Bearer token I provide with the CONNECT call.
Unfortunally it is no longer possible to disable csrf with STOMP as the new Annotation leaves no option for that. So I went and implemented CSRF and just found out the same issue @emopti-jrufer found. It took me days and I finally ended up creating a clean room example last weekend with JWT checking + CSRF to show this error - just to find out an issue has already been opened.
Please work on that asap as basically STOMP Websockets are broken and can't be used.
The 2 fixes (CsrfTokenHandshakeInterceptorFix, exchanging csrfTokenRequestHandler) @emopti-jrufer provided in his example implementation fixed the issue for me as a workaround.
Thanks @emopti-jrufer for the sample! I was able to downgrade your sample to Spring Security 5.8.1 so I can do some testing prior to upgrading to 6.0 and can definitely see the issue. I unfortunately am not very familiar with websocket/stomp support so I missed this.
@OlliL sorry to hear about your troubles. Thanks for verifying the workaround!
Based on my testing, I believe the workaround can be simplified to no longer need the EagerCsrfTokenHandshakeInterceptor from your sample by using the configuration in the 5.8 migration guide under Explicit Configure CsrfToken with 5.8 Defaults. It would look like this:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
requestHandler.setCsrfRequestAttributeName(null); // <-- opt out of deferred tokens in 6.0
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/csrf").permitAll()
.anyRequest().authenticated()
)
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
)
.formLogin((formLogin) -> formLogin
.successHandler(new BasicAuthenticationSuccessHandler())
);
return http.build();
}
// ...
}@emopti-jrufer can you verify that the above configuration removes the need for a custom HandshakeInterceptor in your case?
This one looks like a tricky issue to resolve given the 5.8/6.0 work for CsrfFilter involved introducing new APIs and the messaging layer adds another level of abstraction. I'll let you know when I have an update on this.
I did face the same problem and struggled for some days, thanks @emopti-jrufer for your initial solution! It did work for me as well.
I just tried the solution of @sjohnr removing the custom HandshakeInterceptor and opting out of deferred tokens. It also worked for me, thank you. I'd appreciate an update if the workarounds are not needed anymore.
@sjohnr the solution you provided from a code point is simpler to implement but there is a small downside where the HTTP upgrade HttpServletRequest and HttpServletResponse objects contained in the deferred variants of the CsrfToken will never get released until the WebSocket is closed.
It should be noted that the simpler solution does not stop using deferred tokens but rather triggers them to be resolved right away.
I can also confirm that its working with the remark @emopti-jrufer explained.... Since I already created an example to show the issue I link it here: https://github.com/OlliL/spring-boot-stomp - the fixes are already applied. There is a simple HTML Page served which opens a Websocket-Connection to the running Spring Boot Container to show the issue.... I know its no longer needed but the code was already created by me :)
there is a small downside where the HTTP upgrade
HttpServletRequestandHttpServletResponseobjects contained in the deferred variants of theCsrfTokenwill never get released until the WebSocket is closed.
That is a great point, @emopti-jrufer, thanks for pointing that out!
created an example to show the issue
Thanks @OlliL!
Thanks for verifying @kaans!
@emopti-jrufer (and others) thanks for your patience!
I have created a branch on 5.8.x with a tentative fix for this issue. I have also created a fork of your sample on 5.8.2-SNAPSHOT dependencies. On my branch, you can do the following (see full example):
@Bean
public ChannelInterceptor csrfChannelInterceptor() {
return new XorCsrfChannelInterceptor();
}Would you have a few minutes to try this out in a real application? You can check out my branch and install snapshots to maven local as follows[1]:
git clone git@github.com:sjohnr/spring-security.git && cd spring-securitygit checkout gh-12378-csrf-websocket-stompsdk env(assumes you have SDKMAN! installed)./gradlew -xtest publishToMavenLocal
[1]: On Mac/Linux. Steps for Windows should be similar.
If a 6.0 baseline would be better for you, let me know and I can prepare a branch.
@sjohnr
A 6.0 baseline would be the quickest way for me to test this change.
Ok @emopti-jrufer no problem. The reason I mention it is that we recommend upgrading to Spring Security 5.8 first using the 5.8 migration guide.
Branch of 6.0.x: gh-12378-6.0.x
Following similar steps as above will (currently) build 6.0.2-SNAPSHOT.
@sjohnr
I have tested gh-12378-6.0.x in our application with no obvious issues.
I am assuming you will be setting XorCsrfChannelInterceptor as the default csrfChannelInterceptor for the 6.x code line since the default CsrfTokenRequestHandler is XorCsrfTokenRequestAttributeHandler. This would negate the need for adding the csrfChannelInterceptor bean.
I am assuming you will be setting
XorCsrfChannelInterceptoras the defaultcsrfChannelInterceptorfor the 6.x code line since the defaultCsrfTokenRequestHandlerisXorCsrfTokenRequestAttributeHandler.
That's correct! And good point, I did not do that on this branch but will do so before merging. Would you like me to apply that change so you can test once more, or are you good with the testing as is?
I am good as is.
Uhm.... so I updated to 6.0.2 and removed the EagerCsrfTokenHandshakeInterceptor from my StompEndpoint registry as well as my csrfTokenRequestHandler from my csrf configuration. But even if I didn't - CSRF stopped working....
Judging from XorCsrfTokenUtils.getTokenValue It feels like there is some base64 encoding expected?! I wonder what am I missing
CSRF Config:
.csrf(configurer -> {
// FIX for https://github.com/spring-projects/spring-security/issues/12378
configurer.csrfTokenRequestHandler(new XorCsrfTokenRequestAttributeHandler());
configurer.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
configurer.ignoringRequestMatchers(OPEN_ENDPOINTS);
})
Websocket Config:
@Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
// FIX for https://github.com/spring-projects/spring-security/issues/12378
registry.addEndpoint("/websocket") // .addInterceptors(new EagerCsrfTokenHandshakeInterceptor())
.setAllowedOrigins("*");
}
Edit
I currently have no idea why the server does not emit an Xored CSRF Token but expects one back - could it be an issue with the CookieCsrfTokenRepository I'm using?
So right now I had to explicitly define that bean to get it working -
@Bean(name = "csrfChannelInterceptor")
public ChannelInterceptor csrfChannelInterceptor() {
return new CsrfChannelInterceptor();
}
Any hint what could have caused this issue would be appreciated.
@OlliL, I've updated the migration guide for 5.8 to include the upgrade steps to prepare for 6.0. I also added cleanup steps to the 6.0 migration guide. You should no longer need the workaround with EagerCsrfTokenHandshakeInterceptor and can remove the @Bean for CsrfChannelInterceptor as well.
I currently have no idea why the server does not emit an Xored CSRF Token but expects one back - could it be an issue with the CookieCsrfTokenRepository I'm using?
In your case, it sounds like you would also benefit from reviewing the info in the 5.8 migration guide on using CookieCsrfTokenRepository. This outlines how to deal with deferred CSRF tokens.
@sjohnr the thing is - while using CookieCsrfTokenRepository the raw CSRF token is used for doing all the to-be-secured HTTP communication. So my Web client only has the raw CSRF token available which was delivered by the cookie.
The default ChannelInterceptor which is used for websocket communication is XorCsrfChannelInterceptor though which expects the CSRF token to be in the "encrypted" format. Neither can I sent an "encrypted" CSRF token, nor is it stored in the CsrfTokenRepository in an "encrypted" way.
So I currently see no other way except supplying the CsrfChannelInterceptor bean to be used for handling CSRF websockets. when using the CookieCsrfTokenRepository on the HTTP part.
This is my SecurityConfiguration - The ::handle part is basically the same which is advertised here: 5.8 migration guide but less code ("_csrf" is the default)
This is my WebSocketConfiguration as well as my WebSocketSecurityConfiguration.
All the "hacks" where removed but thats the current setup I need to get it working with the current spring-security.
I see. Thanks @OlliL! There are a number of different permutations possible depending on both your client-side framework and app code, in addition to using websockets. I wasn't considering the fact that you're using an Angular-style integration that obtains the raw token from the cookie itself.
The CookieCsrfTokenRepository example could be modified to set a response header to obtain the hashed token.
Note that there is also an option listed in the migration guide demonstrating an example /csrf endpoint that would provide a way for you to obtain the hashed token, but I do not include an example of how to integrate with that endpoint. If you want to go with an out-of-the-box Spring Security setup, you will need to change your client-side app to obtain the hashed token somehow.
Having said that, it sounds like you found the correct configuration. I'm not sure how much more specific the migration guide can be (your case is probably a common one but still one of many). If you feel there's room for improvement in the docs, feel free to submit an issue with suggestions and I'll happily take a look!
@OlliL @sjohnr I am using Angular as our client-side framework. I ended up with a hybrid approach to use XorCsrfToken that combines using the session as via HttpSessionCsrfTokenRepository for storing the CsrfToken, a /csrf endpoint that is called prior to authenticating which populates the initial XSRF-TOKEN cookie utilized by Angular and finally created an AuthenticationSuccessHandler to repopulate the cookie post authentication since a new CsrfToken is created during authentication. Granted the part could be accomplished by calling the /csrf endpoint again.
The solution could be a lot simpler if it was natively in the framework. For example CsrfAuthenticationStrategy might be a better spot for setting the cookie post authentication.
I can share can the implementation or even contribute the framework if it would beneficial to others.
and finally created an
AuthenticationSuccessHandlerto repopulate the cookie post authentication since a newCsrfTokenis created during authentication
I believe this is true because of HttpSessionCsrfTokenRepository. I donโt believe it is needed in the case of CookieCsrfTokenRepository. Can you explain why you found this necessary?
The solution could be a lot simpler if it was natively in the framework. For example
CsrfAuthenticationStrategymight be a better spot for setting the cookie post authentication.
Can you elaborate on this point? What would be changed in the framework to make this work better for you? Keep in mind that the framework has to support both session and cookie-based CSRF tokens. Also bear in mind that the design consideration for deferred tokens is that the application itself should choose when the token is loaded. I donโt think the framework can make that choice for you.
@sjohnr
Overall I want to minimize the number of requests needed to get to an authenticated state in our application.
/csrf
/authenticate
/csrf (Eliminate the need for this request)
... remaining authenticated application requests
My first attempt at trying eliminate the need for the extra /csrf request using the CookieCsrfTokenRepository was to retrieve the token after the CsrfAuthenticationStrategy was invoked. The following are the options I was evaluating.
- I was not able to add an additional
SessionAuthenticationStrategyafterCsrfAuthenticationStrategysinceorg.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer#addSessionAuthenticationStrategyis package private. - Filter. There wasn't a great way of determining if a request had its token replaced without doing url matching on all authentication endpoints. Having a request attribute could make this cleaner.
- Custom AuthenticationSuccessHandler wrapper.
However all of these options have an issue where the csrf cookie was being set 2 times in the response. One for the clearing of the token in CsrfAuthenticationStrategy and one for the setting of the token which the browser was ignoring the second setter of the same cookie with the new token value.
Possible solutions:
- Add the capability to
HttpSessionCsrfTokenRepositoryto also write a cookie along with adding an option intoCsrfAuthenticationStrategyto eagerly persist the token. It may be necessary to relocate where theXorCsrfTokenRequestAttributeHandler.createXoredCsrfTokenlogic resides. - In the
CsrfAuthenticationStrategyinstead of a clearing out the token (removing session attribute or setting cookie to null) just save the new token value. This could potentially be configurable via a flag that the application can control.
the application itself should choose when the token is loaded. I donโt think the framework can make that choice for you.
Could clarify the following for me? When referring to "loaded" are talking about retrieving the token or generating the new value?
The solution could be a lot simpler if it was natively in the framework.
This is referring to the locked down nature of the framework where certain internal apis are unavailable and classes are not extensible.
Note: The debug statement in CsrfAuthenticationStrategy#onAuthentication is a bit misleading since the token is actually not replaced at that point in time from a persistence (session / cookie) standpoint until something else tries to access the token.
@emopti-jrufer appreciate the extra details!
The behavior you're trying to puzzle out now is actually how to solve for deferred tokens in Spring Security 6, which the 5.8 migration guide covers in detail.
If you follow the guide and run into issues with deferred tokens, you have 3 options for handling deferred tokens:
- Option 1 - Add a filter
- Option 2 - Add an endpoint
- Option 3 - Opt out
If you had followed the upgrade path from 5.7 to 5.8 and performed these steps in order to see which one worked for you (before trying to enable BREACH protection in a later step), you should find that Option 3 solves your problem. This is because in your case, you're using .formLogin() with CookieCsrfTokenRepository. (Granted, when you were trying this, I hadn't yet updated the documentation to include these 3 options, so I'm describing the ideal path going forward that may not have been available for you at the time. ๐)
Next, when you try the steps in the next section to enable BREACH protection, you would find you have two options for handling it:
- Option 1 - Validate raw tokens with Angular
- Option 2 - Opt out (which takes you back to an earlier example configuration)
In your case, you have already found that Option 1 solves your problem. But because it wasn't combined with the solution from the deferred token section, you've struggled to get it working the way you'd like it to.
I recognize that this is a bit of a "choose your own adventure" book, but that's because there are so many possible ways you can choose to integrate the frontend with Spring Security. Your case is one among many.
Having said all that, here is what I believe should work for you:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
CookieCsrfTokenRepository csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
delegate.setCsrfRequestAttributeName(null);
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(delegate::handle)
.csrfTokenRepository(csrfTokenRepository)
)
.formLogin(Customizer.withDefaults());
// @formatter:on
return http.build();
}
}This solution should not require any calls to the /csrf endpoint (in the case of the default login page), since it didn't solve your problem as described above. Though in your case, you may not have any other way of triggering the initial token to be loaded, so it's fine to keep it for the first call.
At this point, I'm not really sure how any of this relates to web socket support. I've included the explanation and example above as an exercise in discovering what (if anything) is missing from our documentation that would help users like yourself (who come into this a bit later than you did and can benefit from better docs). If you feel I'm missing something, please correct me where I'm wrong.
I feel like the main issue here is just following the migration guide top-to-bottom. If this is done, users will hopefully have a better experience. Can you think of anything I'm not considering?
I also missed answering your questions, sorry about that.
the application itself should choose when the token is loaded. I donโt think the framework can make that choice for you.
Could clarify the following for me? When referring to "loaded" are talking about retrieving the token or generating the new value?
Both. The application chooses to trigger loading the token via CsrfToken#getToken(). It is loaded or generated and saved if necessary via the DeferredCsrfToken interface.
The solution could be a lot simpler if it was natively in the framework.
This is referring to the locked down nature of the framework where certain internal apis are unavailable and classes are not extensible.
All of the components you mentioned can be extended through delegation. You can use CsrfConfigurer#sessionAuthenticationStrategy instead of SessionManagementConfigurer#addSessionAuthenticationStrategy. It is possible to achieve your desired results this way, but the configuration I provided above is simpler.
@sjohnr Is there a reason this information would be documented in a migration guide? Isn't this information still relevant in the latest version? We started on Spring 6 so following a migration guide for prior versions seems counterintuitive. That said I will try out your suggestions and if needed open separate issues or post on Stack Overflow.
Is there a reason this information would be documented in a migration guide? Isn't this information still relevant in the latest version?
Thanks for the question, @emopti-jrufer! So as not to encourage a forum-like discussion here, I'll keep my answer brief.
Since there were numerous highly impactful CSRF-related changes in Spring Security 6, I wanted to address upgrade issues first. Any positive feedback (this is usually rare ๐) would be used to make improvements on the main documentation for CSRF as time allows. Documentation improvements are a theme we're focusing on, so now is a good time to address it.
We're also always looking for individuals who would be interested in making improvements to our documentation, as it tends to be an easy way to get involved with open source without a huge time commitment. If you're interested in helping, let us know by chiming in on an issue! I'll open a new issue for CSRF documentation enhancements when I get some time this week.
@sjohnr Sry for using this as a forum-like discussion, but I need to know if the configuration is the right approach for an reactive SecurityWebFilterChain as it's not mentioned in the documentation:
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
XorServerCsrfTokenRequestAttributeHandler delegate = new XorServerCsrfTokenRequestAttributeHandler();
http.csrf(csrf -> csrf
.csrfTokenRepository(cookieCsrfTokenRepository())
.csrfTokenRequestHandler(delegate::handle))
.formLogin(Customizer.withDefaults());
I'm asking as there's no way to call setCsrfRequestAttributeName(null) on the reactive impl.
@HJK181, is this what you're looking for? https://docs.spring.io/spring-security/reference/5.8/migration/reactive.html#_i_am_using_angularjs_or_another_javascript_framework
Shame on me, did not see the reactive menu entry on the left-hand side, thx a lot.
Does this mean the websockets are broken in the 5.x branch? Any chance to have this ported to 5.x?
@ptahchiev not that I'm aware of. If you find anything, please open an issue. Please note that this support was added to 5.8 (see gh-12562).
To disable CSRF with stomp spring boot 3 just add this code in your configuration class, the bean name must be csrfChannelInterceptor :
@Bean
public ChannelInterceptor csrfChannelInterceptor(){
//disabling csrf
return new ChannelInterceptor() {
};
}
assuming that you are using @EnableWebSocketSecurity
