Securing WebSocket applications on Glassfish

Today we are going to cover deploying secured WebSocket applications on Glassfish and access to these services using WebSocket Client API.

WebSocket server application setup

Our server endpoint might look as simple as this:

@ServerEndpoint("/echo")
public class EchoEndpoint {

  @OnMessage
  public String echo(String message) {
    return message + " (from your server)";
  }
}

Everything else must be configured on container level.

We can start with enabling SSL, which will require web.xml to be added to your project. For starters, it might look as following:

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee">
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Protected resource</web-resource-name>
      <url-pattern>/*</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>

    <!-- https -->
    <user-data-constraint>
      <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
  </security-constraint>
</web-app>

This is minimal web.xml for this task - web-resource-collection just defines URL pattern and HTTP method(s) we want to put a constraint on and user-data-constraint defines that constraint, which is in our case transport-guarantee. More information about these properties and security settings for web application can be found in Oracle Java EE 7 Tutorial.

I have some simple webpage attached as well, so I can test my endpoint right away. You can find it (along with complete project) in Tyrus workspace: [webpage] [whole project].

After deploying this application to Glassfish Application Server, you should be able to hit it using your favorite browser. URL where my application resides is https://localhost:8181/sample-echo-https/ (may be different, depends on other configuration). My browser warns me about untrusted certificate (I use what freshly built Glassfish provides - self signed certificates) and after adding an exception for this site, I can see my webpage and I am able to securely connect to wss://localhost:8181/sample-echo-https/echo.

WebSocket client

Already mentioned demo application also contains test client, but execution of this is skipped for normal build. Reason for this is that Glassfish uses these self-signed "random" untrusted certificates and you are (in most cases) not able to connect to these services without any additional settings.

Creating test WebSocket client is actually quite similar to server side, only difference is that you have to somewhere create client container and invoke connect with some additional info. Java API for WebSocket allows you to use annotated and programmatic way to construct endpoints. Server side shows the annotated case, so let's see how the programmatic approach will look.

final WebSocketContainer client = ContainerProvider.getWebSocketContainer();
client.connectToServer(new Endpoint() {
  @Override
  public void onOpen(Session session, EndpointConfig EndpointConfig) {
    try {
      // register message handler - will just print out the
      // received message on standard output.
      session.addMessageHandler(new MessageHandler.Whole<String>() {
        @Override
        public void onMessage(String message) {
          System.out.println("### Received: " + message);
        }
      });

      // send a message
      session.getBasicRemote().sendText("Do or do not, there is no try.");
    } catch (IOException e) {
      // do nothing
    }
  }
}, ClientEndpointConfig.Builder.create().build(),
   URI.create("wss://localhost:8181/sample-echo-https/echo"));

This client should work with some secured endpoint with valid certificated signed by some trusted certificate authority (you can try that with wss://echo.websocket.org). Accessing our Glassfish instance will require some additional settings. You can tell Java which certificated you trust by adding -Djavax.net.ssl.trustStore property (and few others in case you are using linked sample).

Complete command line when you are testing your service might need to look somewhat like:

mvn clean test -Djavax.net.ssl.trustStore=$AS_MAIN/domains/domain1/config/cacerts.jks\
    -Djavax.net.ssl.trustStorePassword=changeit -Dtyrus.test.host=localhost\
    -DskipTests=false

Where AS_MAIN points to your Glassfish instance.

Note: you might need to setup keyStore and trustStore per client instead of per JVM; there is a way how to do it, but it is Tyrus proprietary feature: http://tyrus.java.net/documentation/1.2.1/user-guide.html#d0e1128.

And that's it! Now nobody is able to "hear" what you are sending to or receiving from your WebSocket endpoint.

There is always room for improvement, so the next step you might want to take is introduce some authentication mechanism (like HTTP Basic or Digest). This topic is more about container configuration so I'm not going to go into details, but there is one thing worth mentioning: to access services which require authorization, you might need to put this additional information to HTTP headers of first (Upgrade) request (there is not (yet) any direct support even for these fundamental mechanisms, user need to register Configurator and add headers in beforeRequest method invocation). I filed related feature request as TYRUS-228; feel free to comment/vote if you need this functionality.

Comments:

Constraint looks wrong. Listing GET means all other methods are not constrained. So a non SSL request with another verb might allow bypass. Check "verb tampering"

Posted by guest on August 02, 2013 at 07:53 PM CEST #

Well, websocket protocol uses only "GET" method, so it is ok (at least for this simple case). But thanks for pointing that out, it could help others when developing some "real-world" applications.

Posted by Pavel on August 02, 2013 at 08:02 PM CEST #

Hi,

I'm using glassfish 4.0 to develop an application that uses websockets. Everything works fine without ssl

var webSocket = new WebSocket('ws://mydomain:8080/websocketserver/websocket/sessionXXX');

However, when i try to use ssl the server does not execute the @OnOpen method and the client shows readyState = 3. Connection is never opened. This is what i'm using to open the connection on the client side.

var webSocket = new WebSocket('wss://mydomain:8181/websocketserver/websocket/sessionXXX');

I've tried a test page on https://mydomain/websocketserver/index.html and ssl is working fine with http. Thus, where can the problem be?

Thanks

Posted by guest on October 24, 2013 at 01:36 PM CEST #

Hello,

it might be lot of things. Best way to debug is to open your browsers javascript console, it usually contains some useful error message. Most of the time it is about SSL certificates - that can be always fixed by adding your server certificate to browsers test store.

Also you could try sample-echo-https from Tyrus workspace, just to be sure that you don't have anything wrong in your app. Feel free to send a note to users@tyrus.java.net, you should get prompt answer there.

Regards,
Pavel

Posted by Pavel Bucek on October 31, 2013 at 09:30 AM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Pavel Bucek

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today