Michal
Michal

Reputation: 371

Websocket server: how to authorize clients?

I am currently building a webpage with a game using html5 canvas and websockets. I have a websocket server written in perl running on server:3000. On the server I have also installed apache, and made a website that the user can visit: http://server/. javascript on this page creates a websocket to ws://server:3000

Connection works and I can send messages from my browser and get the responses, everything shown in a div for now. The messages that I send are for now only "message:...", so the server knows that it is a message and outputs '...' to all users currently connected to the "game".

Now the problem is that anyone could write their own client that would connect to ws://server:3000 and send/receive appropriate messages. This way they would use my backend, but not my client (I would like them to play only from http://server/).

How can I ensure that only my client is used? The main concern is cheating. You could write a bot that connect to a port and plays for you by sending messages and intepreting the ones that it receives...How to overcome this issue?

Upvotes: 29

Views: 35254

Answers (9)

user9903
user9903

Reputation:

Basic Auth works as indicated by this answer here: HTTP headers in Websockets client API

With Basic Auth you can include the username and password in the URL:

ws://username:password@localhost:8888/

Alternatively you can implement your own security just sending hashed/md5/sha1 passwords through the protocol and a username or some other session identifier. Of course it's good to have a token request before all that the client also passes up.

Upvotes: 0

robx
robx

Reputation: 2329

As others have remarked, you won't be able to enforce that only your own client-side code talks to your websocket. However, you can tie websocket connections to the rest of your web server.

For example, suppose you authenticate your players via a login form served by your web server. Then on successful login you generate a token that you hand out to the client, and have your client code pass the token as a query parameter that you can check against in your websocket connection handler:

  1. user posts username/password to http://server/play
  2. generate a random token, put it in a database as (token, username, timestamp)
  3. generate and serve an HTML document that loads your game's Javascript code, and passes this token (e.g. the body contains myGame.start(<literaltoken>)
  4. your game's Javascript connects to ws://server:3000/?token=<token>
  5. your server looks up the token and can tie the connection to the user, and/or reject the connection if the token is not known or too old

If you want to avoid having to keep state server-side, you could instead make the token an encrypted and signed blob of (username, timestamp), using some secret server-side key, and decrypt/verify that blob in the websocket handler.

Upvotes: 1

Periata Breatta
Periata Breatta

Reputation: 498

What you are asking for is impossible to achieve. You can take steps to make it difficult for a third party to make an unauthorised client, but in the end as long as the third party is able to use your legitimate client, they can always watch what it does and copy it, and there is nothing you can do to stop that.

There are things that you can do to make it harder, though:

  • Deliver your client as obfuscated, encrypted javascript.
  • Use encryption to make the messages you send and receive through your websocket interface difficult for a casual hacker to interpret.

But even if you do this, a determined attacker can decrypt and deobfuscate your javascript, find the key you're using to encrypt the messages, and then revert them to plain text and reverse engineer what they do.

Upvotes: 1

nilskp
nilskp

Reputation: 3127

Do login/authentication the usual way and then include the session id in the web socket upgrade request.

Upvotes: -1

Christian Lacdael
Christian Lacdael

Reputation: 141

I'm using a Jetty web server.

I am using Basic authorisation. I used this at first simply because it was easy to implement. However, after discovering the authorisation issue presented by websockets, I think basic authorisation is a good mechanism to use.

The upgrade request header has authorisation data hashed in it:

Accept-Encoding:gzip, deflate, sdch
Accept-Language:en-GB,en-US;q=0.8,en;q=0.6
Authorization:Basic YWRtaW46cm9vdA==
Cache-Control:no-cache
Connection:Upgrade

In my websocket server, with each OnMessage event I call:

session.getUpgradeRequest().getUserPrincipal()

I can then use this information to decide whether the data is authorised to be processed. Also, I check the origin of the socket in the OnConnect event:

 session.getUpgradeRequest().getOrigin()

Upvotes: 2

Kurt Pattyn
Kurt Pattyn

Reputation: 2798

I propose you have a look at how other services do this. See for instance pusher.com, specifically the private channels they use.
You can learn more about their protocol at http://pusher.com/docs/pusher_protocol.
You can have a look at the source code of their client libraries as well.

Upvotes: 0

securecurve
securecurve

Reputation: 5817

Very simple I guess, add a header on websocket connect, let it be hostname or something, and assign to it some cryptic value, and check this cryptic value everytime a websocket connection is made against your webscoket servers.

Upvotes: 0

Sergey Birukov
Sergey Birukov

Reputation: 91

I came to the same problem. I'm writing websocket->socket proxy to make HTML5 client for our JAVA/Flash game. They use self-made encryption at authorization stage to make sure it is legacy client.

I don't think it is secure to insert the same encryption code as the sources of js file are open. For now I hid that auth algorithm in my WS proxy, but I need to somehow detect unauthorized WS clients now :)

I'm using temporary session ids from the web site to check if a websocket client is authorized on the web site. It is not reliable though.

Also I'm thinking on some mechanism to make a client (browser) to download dynamically generated javascript file, run it, and send to websocket server, that can check for the valid result. That way I can be sure that the client is browser (or complicated bot with JS engine :) ). Anyway, that are just my thoughts and it seems hard to implement, I didn't try that yet.

Upvotes: 0

Shiv Kumar
Shiv Kumar

Reputation: 9799

The nature of the web is such that this sort of thing becomes easier (for the hackers) and harder for those building applications/websites. Normally you'd use "referer" to sort of ensure that the request is coming from your site/domain. It's not fool proof but works for the majority of cases.

In Html 5 I know hyper links don't have to send the referer. That could change to include websockets and the like as well.

Even if you were to encrypt your message with a salt of some sort and send the hash of it along with your message to validate the message against the hash on the server, your client side code is potentially available and visible to anyone so they can crack your logic.

The WebSocket spec has been is constant flux. Some browsers have either disabled or removed their WebSocket support for the time being.

The WebSocket handshake protocol (using Sec-WebSocket-Key1 and Sec-WebSocket-Key2) was supposed to make things bit secure but this spec won't work through most proxy servers. You can read the spec here http://www.whatwg.org/specs/web-socket-protocol/

Note that the handshake protocol was introduced for a very different purpose. That is not what you're after but rather to prevent a client from hacking a server such that unless the server responds with a handshake the client can't communicate with the server. But in your case you'll need to respond with a handshake without knowing the client.

So I don't think there is a reliable way yet to do what you need.

Upvotes: 0

Related Questions