Search code examples
javacross-domaincsrfjava-websockettyrus

Origin check in Java Websockets to prevent CSRF


I am programatically deploying a Java Websocket endpoint (JSR356 3.1) and I want it to validate the Origin request header value in order to mitigate CSRF attacks, and only accept handshake requests whose Host and Origin header values match.

It seems to me that the way to go is overriding the method:

ServerEndpointConfig.Configurator.checkOrigin(String originHeaderValue)

(the implementation provided by Tomcat 8 always returns true), but the problem I see is that this method only has a single parameter, and I miss the host header value to compare this value against.

Also this class do not offer any method returning this information, so I see this method as useless, unless you are comparing the origin header value against a previously known host value or set of values, but this doesn't have any sense too, since my application could be seen under different, changing host names or IPs.

I am considering performing this validation inside the implementation of modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) where I can read all header values, but I am pretty sure that I am misunderstanding something.

So my question is:

What is the proper way to perform this validation?

Thanks, Nacho


Solution

  • After digging a little more into the problem I have concluded that the Websocket API (JSR-356) is designed to perform an origin check against a "white list", a series of predefined allowed origins (as you can see in this example from Weblogic):

     ...
     import javax.websocket.server.ServerEndpointConfig;
    
     public class MyConfigurator extends ServerEndpointConfig.Configurator {
     ...  
         private static final String ORIGIN = "http://www.example.com:7001";
    
         @Override
        public boolean checkOrigin(String originHeaderValue) {
            return ORIGIN.equals(originHeaderValue)   
        }
     }
     ... 
    

    but unfortunately, this API do not contemplates the possibility of implementing a Host vs Origin header validation.

    As a workaround, for those service providers that place the websocket endpoint behind the servlet stack (like Tomcat, where the Websocket machinery is triggered by the servlet filter org.apache.tomcat.websocket.server.WsFilter) you can create an outer filter that enables threadlocal access to the request, in order to use it in the checkOrigin() method to get the Host header value.

    This makes me wonder why this type of checking is not preferred over the white list solution, and just found this interesting post: Origin and Host headers for same domain requests