2

In my web application, the encryption is not made by the application server, but by the reverse proxy. I'd like to keep it that way. The relevant technology stack is the following:

  • Application server: payara 5.181 full
  • Java EE 8/JSF 2.3; Mojarra 2.4.0-m01.payara-p5

Due to the proxy encryption, the appserver is configured to not use encryption within the web.xml:

<user-data-constraint>
    <transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>

Now, the websocket connection fails to work with active proxy, because it is beeing established in non-encrypted way (ws://...) by the client. This is blocked by the browser when the content is delivered already in https.

In my understanding, both the behaviour of the browser and the appserver seems correct. While the browser avoids downgrading, section 8.3 "Transport Guarantee" of the websocket specification states:

A transport guarantee of NONE must be interpreted by the container as allowing unencrypted ws:// connections to the websocket [WSC-8.3-1]

Is there a way to force encryption (the use of wss://) for the f:websocket, although using transport guarantee NONE on the app server? Or is there another setting or better approach for this?

For the moment, I could not find a way to adapt the behaviour of f:websocket and had to switch back to the use of plain javascript websocket in combination with classic websocket serverendpoint.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
LTrenker
  • 71
  • 5

1 Answers1

4

Is there a way to force encryption (the use of wss://) for the f:websocket, although using transport guarantee NONE on the app server?

The websocket URL is encoded via ExternalContext#encodeWebsocketURL(). As usual in JSF, pretty everything can be decorated or even replaced via factories, so also the ExternalContext.

Below is a kickoff example which blindly replaces ws:// in websocket URL to wss://.

public class CustomExternalContext extends ExternalContextWrapper {

    public CustomExternalContext(ExternalContext wrapped) {
        super(wrapped);
    }

    @Override
    public String encodeWebsocketURL(String url) {
        return super.encodeWebsocketURL(url).replaceFirst("ws://", "wss://");
    }

    public static class Factory extends ExternalContextFactory {
        public Factory(ExternalContextFactory wrapped) {
            super(wrapped);
        }

        @Override
        public ExternalContext getExternalContext(Object context, Object request, Object response) throws FacesException {
            return new CustomExternalContext(getWrapped().getExternalContext(context, request, response));
        }
    }
}

In order to get it to run, register it as below in faces-config.xml:

<factory>
    <external-context-factory>com.example.CustomExternalContext$Factory</external-context-factory>
</factory>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • It worked right away, thank you for the detailed elaboration. As an addition to the original question, would the distinction be possible also on the client? In the jsf.js:jsf.push the function `d.location.protocol.replace("http", "ws") + "//"` is contained and executed, but the result doesn't seem to be used further. This function would exactly fit the purpose. The advantage beeing that it works for http as well as https without parametrization.. – LTrenker Apr 05 '18 at 20:21
  • Yes, that was the initial implementation, which was taken over from ``. And then the JSF EG decided that the full URL should better be generated in server side via a public API, so that it can more easily be adjusted by e.g. a plugin library. The mentioned function in jsf.js is a leftover and should actually have been removed (reference: I wrote the `` and `` myself). – BalusC Apr 06 '18 at 06:42
  • That is a real pity... Thanks for the first class insights, anyway. – LTrenker Apr 06 '18 at 19:29
  • Many Thanks for this valuable information. I got it running straight away ! – user1755189 Sep 19 '22 at 13:41