How to mix csrf enabled and disabled spring websockets in one application

Viewed 170

I have an application where I need two different websocket setups:

  1. one for allowing communication between the application and remote Java-based clients
    • uses stateless comms (auth token is included in each request, also in the websocket connect request)
    • csrf needs to be disabled
  2. one for allowing async push notifications from the application to its own web UI.
    • uses normal session authentication
    • csrf needs to be, or should preferably be, enabled (correct me if I'm wrong?)

Now, in Spring, to disable cross origin checking for websockets one needs to extend AbstractSecurityWebSocketMessageBrokerConfigurer e.g. as follows:

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
    @Override
    protected void configureInbound(final MessageSecurityMetadataSourceRegistry messages) {
        messages.anyMessage().authenticated();
    }

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

The question is, how can I have it disabled for some websockets and enabled for others?

1 Answers

If you take a look at the method AbstractSecurityWebSocketMessageBrokerConfigurer#configureClientInboundChannel(ChannelRegistration), when you have the sameOriginDisabled set to false, it simply register a CsrfChannelInterceptor:

if (!sameOriginDisabled()) {
    registration.setInterceptors(this.context.getBean(CsrfChannelInterceptor.class));
}

And after that, it calls the customizeClientInboundChannel(ChannelRegistration) method.

I can't test now, but I think you can override the method customizeClientInboundChannel(ChannelRegistration) and do the following:

@Override
protected void customizeClientInboundChannel(ChannelRegistration registration) {
     registration.addInterceptor(myCustomCsrfChannelInterceptor());
}

private CsrfChannelInterceptor myCustomCsrfChannelInterceptor() {
    return new MyCustomCsrfChannelInterceptor();
}

private static class MyCustomCsrfChannelInterceptor {
    private MessageMatcher<Object> matcher = //create your MessageMatcher with your rules

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        if (!this.matcher.matches(message)) {
            return message;
        }
        //copy the content from `CsrfChannelInterceptor`
    }
}

In summary, what you are doing is creating a custom CsrfChannelInterceptor that will use a custom MessageMatcher with your own rules to check if it should apply to that Message, and the rest is just a copy of the original interceptor.

Related