4

Background

I am designing a multi-player game with a single server that handles multiple worlds. Each player logs into the server initially before requesting which world to join.

The server has a fixed IP address that is currently the same as the site's IP address. This may change in future if servers need to be ran in parallel, however as of now it is a safe assumption that the server is, in fact, the server, and not an imposter.

Passwords on my site are hashed using bcrypt and a salt, and stored in a MySQL database.

I do not currently have an SSL certificate, though I am planning to get one ASAP and certainly before any payment details are handled by the site, but the game is still likely to be connecting over an open TCP connection, so my security needs with respect to authenticating game users needs to be handled manually.

Audience

The main audience of the game is young teens, but particularly those able to write a little code, so I can expect a small amount of them to be relatively competent programmers and a smaller subset to be able to reverse engineer and break into an insecure system.

My currently considered approach

I spent a while thinking about this issue myself and came up with a solution that appears to be classified under a "Challenge-response" algorithm, that is:

  1. Client sends username
  2. Server receives & verifies username, and sends a request for password data along with a unique token. My current idea for this unique token is to generate a random string, MD5 it, then prepend a unique, incrementing integer to ensure both that the same hash is never sent, and that it is not predictable.
  3. Client receives token, hashes password using bcrypt with the known salt to get the value in the website database, and then appends the token before running it through bcrypt again.
  4. Client sends back this new hash over the socket
  5. Server reads this, verifies it, and notifies the client that it is now authenticated as the username they initially sent.

Aims

No device but the true client and the server should be able to find out the password, or authenticate by eavesdropping for exchanged sensitive data.

Worries

I have a particular issue with this approach in that now the secret salt I have been using on my web-site to hash passwords is going to have to be known by clients. I am struggling to think of a reason this is compromising to my security, but it feels wrong. I suppose, in theory, they can now pre-generate rainbow tables then if they managed to obtain a copy of my database, there would be a much lower amount of time between them obtaining it, and cracking a decent number of user's passwords, meaning that my users accounts are at a larger risk as they will have less warning should anything happen.

Outside of this, though, I also would appreciate greatly an evaluation of my considered approach and any pointers or links that may be helpful to securing it more.

I am more than happy to answer any questions! Thanks!

  • In creating the unique token, is the incrementing integer needed, as you already are using an random source for the seed of MD5? Also, SHA256 is much more secure choise for that task. – Zokol Aug 09 '15 at 21:07
  • I figured my token had two aims -- To be unpredictable (That is, a user could not just add one to the last auth request to anticipate the next one), which the random MD5 characters achieve, and to be unqiue (So a middleman could not build a dictionary of Auth token -> Server's desired response for a user), which applying an incrementing integer achieves. I didn't think it was important for this token to be ran through any complicated hashing algorithm (I'd even struggle to justify MD5). I could well be missing something, though! – Ashley Davies Aug 09 '15 at 21:15
  • So you are not generating the seed number for token each time, but just incrementing it between tokens? – Zokol Aug 09 '15 at 21:33
  • Also, concidering the password salt, you could use different salt for each user. This would help the malicious actor only to know the salt used for his own password, but not for everyone. Distributing the salts to clients is of course a bit more complex task than using same salt for everyone. – Zokol Aug 09 '15 at 21:38
  • My thoughts was the token could be {Incrementing integer}{MD5 seeded with something random} - then it remains unique & unpredictable. The salt is built into quite a few user accounts already, and the framework I'm using on the web end I don't think is built to support unique salts. I could hack at it and get it to work but if there's any alternative, that's preferable. – Ashley Davies Aug 09 '15 at 21:44
  • Actually - thinking on it, I could add two fields to the database: A unique salt, and a second hashed password - the latter using the unique salt instead of the standard salt. This fact wouldn't be revealed to users & both would be updated at the same time, but the website would log-in with one and the game the other. Is there any significant cons evident with this approach outside of the little extra code I'd need to write? – Ashley Davies Aug 09 '15 at 21:48
  • 3
    Don't roll your own crypto. Use TLS. – Martin Schröder Aug 13 '15 at 20:29
  • 2
    You system could employ TLS even if you don't buy it from a certification authority. Just create your own self signed certificate, hard code it on the client and you are set. – ThoriumBR Aug 17 '15 at 17:57

2 Answers2

3

As Martin suggested in the comments, I would highly suggest using TLS. Even if your audience is young teens, disclosing the salt to the client is not a good idea.

For example, what would happen if the salt for the hashes is hard-coded into your web application and an attacker exploits a vulnerability to extract the bcrypt password hashes? If the attacker can determine the salt from the client, then the attacker can start cracking the password hashes with less effort.

Some good resources on why you shouldn't roll your own crypto can be found here and here.

infosec
  • 331
  • 1
  • 5
  • 1
    Fair enough -- I think I'll go with TLS in this case, thanks for the reasoned response! Would you recommend doing anything to the password before sending over TLS or will sending it plaintext (Albeit presumably encrypted by the protocol) be sufficient? – Ashley Davies Aug 17 '15 at 17:42
0

I agree that using TLS to encrypt the whole authentication exchange would be the best solution. If you think TLS would be too over-sized for the scope you intend, consider the following:

  • Using the password as a shared secret in a challenge/response scheme doesn't expedite you from having to pass said shared secret from the client to the server the first time, when the user creates an account.

  • You can use a simple asymmetric encryption (server sends public key to the client, client encrypts password, server decrypts it using secret key), but that doesn't save you from man-in-the-middle attacks.

When you use TLS:

  • Have the server and client agree on ciphers that are reasonably safe, even for the forseeable future.

  • Have the client do a certificate-based authentication as well before establishing the connection. It's not just the server proving itself to the client that it is legit - the client itself should need to prove this to the server as well. While this does not prevent malware from tampering with the client's code in-memory and have it still show up to the server as legit, it would put another layer of security against MITM attacks.

  • You may want to have the server (and client) certificate not simply self-signed ones, but signed by an 'official' trust anchor (like VeriSign) and use CA pinning to protect yourself against MITM proxies swapping out the certificates as ready-made SSL sniffer proxies already do.

This way you should be reasonably protected against attacks on the data in transit, and making the client and server (software and hardware) tamper-proof themselves was not scope of this question.

TL;DR: "I'll use TLS" alone is not the be-all, end-all means to do it - there are a number of pitfalls which could make a TLS encrypted connection still vulnerable.

Anonymous
  • 101