Home > Software engineering >  Setting up CSRF for spring websocket
Setting up CSRF for spring websocket

Time:01-12

I am building an application where authentication is done by spring security for HTTP handlers, for HTTP I've disabled csrf protection, and now I want to disable csrf for spring web socket, but I can't figure out how to accomplish this, I've already tried many different approaches but no one seems to be working. If it is impossible to disable csrf for WebSocket how to get a csrf token? (I tried setting up the csrf endpoint to obtain a token but it is not work, and all tutorials I've found are outdated) Thanks in advance!

web socket security config:

@Configuration
@EnableWebSocketSecurity
public class WebSocketSecurityConfig extends    AbstractSecurityWebSocketMessageBrokerConfigurer {
@Bean
AuthorizationManager<Message<?>> messageAuthorizationManager(
  MessageMatcherDelegatingAuthorizationManager.Builder messages)   {
messages.anyMessage().permitAll();

return messages.build();
}

@Override
    protected boolean sameOriginDisabled() {
    return true;
  }
}

security config:

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {

  @Autowired
  private JwtFilter jwtFilter;

  @Bean
  SecurityFilterChain securityFilterChain(HttpSecurity HTTP)  throws Exception {
    return http.addFilterBefore(jwtFilter,   BasicAuthenticationFilter.class)
        .cors(AbstractHttpConfigurer::disable)
        .csrf(AbstractHttpConfigurer::disable)
        .authorizeHttpRequests(auth -> auth
        .requestMatchers("/authenticate").permitAll()
        .requestMatchers("/createchatroom").authenticated()
        .requestMatchers("/public/*").permitAll()
        .requestMatchers("/private/*").permitAll()
        .requestMatchers("/ws/**").authenticated()
        .requestMatchers("/register").permitAll()
        .requestMatchers("/csrf").authenticated()
         .requestMatchers("/addEmployeeToFavorites").hasAnyAuthority(EMPLOYEE.name(),
            ADMIN.name())
        .requestMatchers("/addChatRoomToFavorites")
        .hasAnyAuthority(EMPLOYEE.name(), ADMIN.name())
        .requestMatchers("/home").hasAnyAuthority(EMPLOYEE.name(), ADMIN.name()))
    .build();
  }
}

CodePudding user response:

By default, Spring Security requires the CSRF token in any CONNECT message type. This ensures that only a site that has access to the CSRF token can connect. Since only the same origin can access the CSRF token, external domains are not allowed to make a connection.

Spring Security 4.0 has introduced authorization support for WebSockets through the Spring Messaging abstraction.

In Spring Security 5.8, this support has been refreshed to use the AuthorizationManager API.

To configure authorization using Java Configuration, simply include the @EnableWebSocketSecurity annotation and publish an AuthorizationManager<Message<?>> bean or in XML use the use-authorization-manager attribute. One way to do this is by using the AuthorizationManagerMessageMatcherRegistry to specify endpoint patterns like so:

@Configuration
@EnableWebSocketSecurity

public class WebSocketSecurityConfig {
@Bean
AuthorizationManager<Message<?>> messageAuthorizationManager(MessageMatcherDelegatingAuthorizationManager.Builder messages) {
    messages
            .simpDestMatchers("/user/**").authenticated()

    return messages.build();
    }
}
  1. Any inbound CONNECT message requires a valid CSRF token to enforce the Same Origin Policy.
  2. The SecurityContextHolder is populated with the user within the simpUser header attribute for any inbound request.
  3. Our messages require the proper authorization. Specifically, any inbound message that starts with "/user/" will require ROLE_USER. Additional details on authorization can be found in [websocket-authorization]

At this point, CSRF is not configurable when using @EnableWebSocketSecurity, though this will likely be added in a future release.

To disable CSRF, instead of using @EnableWebSocketSecurity, you can use XML support or add the Spring Security components yourself, like so:

Java

@Configuration
public class WebSocketSecurityConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(new AuthenticationPrincipalArgumentResolver());
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        AuthorizationManager<Message<?>> myAuthorizationRules = AuthenticatedAuthorizationManager.authenticated();
        AuthorizationChannelInterceptor authz = new AuthorizationChannelInterceptor(myAuthorizationRules);
        AuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(this.context);
        authz.setAuthorizationEventPublisher(publisher);
        registration.interceptors(new SecurityContextChannelInterceptor(), authz);
   }
}

web.xml

<websocket-message-broker use-authorization-manager="true" same-origin-disabled="true">
    <intercept-message pattern="/**" access="authenticated"/>
</websocket-message-broker>

On the other hand, if you are using the legacy-websocket-configuration and you want to allow other domains to access your site, you can disable Spring Security’s protection. For example, in Java Configuration you can use the following:

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

...

    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }
}

References

  • Related