Search code examples
javacometcometd

CometD Secure Requests


I am using CometD and I have a service setup (Java on the server side) as follows:

http://localhost:8086/service/myService/get-player-details?params={id:1234}

This works fine in practice but what concerns me is that any user can query my service using the above URL and retrieve another players details.

What would be the suggested way of guarding against such an issue? Would authorizers be the correct approach?


Solution

  • If the URL you posted is a mapped to CometD, then I strongly discourage you to use those kind of URLs to pass information such as params in the URL.

    First, this will not work if you use other transports that are not HTTP, such as WebSocket.

    Second, as you note that URL may expose information that you don't want to expose.

    I recommend that you change the way you retrieve information from the server to not use URLs but only messages.

    If all your communication with the server happens via messages, CometD on the server already validates that the message comes from a client that was allowed to handshake. You just need to enforce the right authentication checks at handshake time using a SecurityPolicy as explained the in authentication section.

    The messages will have this form, using the CometD JavaScript client library:

    cometd.publish("/service/player", { action:"get", playerId: 1234 });
    

    There may be variations of this pattern where you want to put the "action" into the channel itself, for example:

    cometd.publish("/service/player/get", { playerId: 1234 });
    

    In this way, you have more little services (each responding to a different channel and therefore to a different action), which may be desirable.

    Reading the examples of the services section may give you additional information.

    I don't recommend to put the playerId into the channel for two reasons:

    • to avoid to create too many channels
    • to have this information promptly available in the code, so you don't need to parse the channel (although CometD support use of parameters in channels); parsing is more costly than just doing message.get("playerId").

    To send the response to the client, the server can just call:

    @Service
    public class GetPlayer
    {
        @Session
        private LocalSession sender;
    
        @Listener("/service/player/get")
        public void perform(ServerSession session, ServerMessage message)
        {
            Map<String, Object> player = retrievePlayerInfo(message.get("playerId"));
            session.deliver(sender, message.getChannel(), player);
        }
    }
    

    Note usage of ServerSession.deliver() to return the response to that specific client.

    What above guarantees you (with a proper SecurityPolicy) that only authenticated clients can send and receive messages.

    What you need to do now is to put in place the right authorizations, in particular that player 123 cannot play as player 789 by hacking the CometD messages that it sends. This is the job for Authorizers, see the section and the examples in the Authorizers documentation.

    What you must do is to establish a relation between the user that authenticated with the playerIds that she's allowed to see. That is application specific and it's the core of your Authorizer implementation.

    With proper SecurityPolicy and Authorizers in place, your application is safe from the concerns of your question.

    Strictly speaking, Authorizers may be enough, but typically if you want an authorization policy to be enforced, you also need authentication, which is provided by the SecurityPolicy.