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?
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:
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
.