hplus' Authentication for Games

Started by
11 comments, last by hplus0603 10 years, 7 months ago

Hi,

I am making a persistent online multi-player game and am currently looking at basic security. I have some questions after reading this:

http://www.mindcontrol.org/~hplus/authentication.html

This is where I'm at so far:

  • By day I'm a .NET web developer and this project is all in C#.
  • Main game communications happen over UDP, provided by lidgren.
  • Lidgren provides per-message encryption options (Xtea, AES, DES, Triple DES, RC2...).
  • I already have Awesomium integrated into my client to provide the user interface.
  • A web service provides the back end for non-action related UI such as mail and trade. It always runs over HTTPS.
  • The web server and game server share a SQL database.
  • Passwords are salted, hashed (bcrypt), and stored in the database.
  • I plan to also use the secure web service as the login server.

All that is reasonably straight forward and I've done most of it before. My questions are when I try and work through the 'sufficient implementation' provided in hplus' conclusion:

  1. Client connects to game server and transmits the auth ticket. Doesn't this already need to be encrypted to prevent against sniffing on open networks? But the server apparently doesn't retrieve the encryption key till later, so what is stopping someone else grabbing the ticket and connecting as you?
  2. Is there any reason not to start the sequence numbers at 0 given we already using a random encryption key?
  3. Aren't sequence numbers going to be fairly useless with UDP unreliable messages?
  4. What is a suitable hash for ongoing messages once authentication has been established? I assume this is really just a checksum to make sure the data hasn't been tempered with.

Also, would you still give the same advice today? Reading the archives here I get the feeling should really be looking at TCP/TLS instead, if at all possible...

Thanks for any help you can offer.

Advertisement

Just a note on goals: I am mostly interested in protecting players accounts from being stolen etc here. Someone reading information in transit is fine, so long as they can't modify it, spoof it or use it to log themselves in etc.

1: That is kinda weird, most systems I have seen, first send some kind of 'handshake' with cryption keys. After that the game packets are send.

2/3: you can also use seq numbers in a different way than 1-2-3-4, but make a pattern like 1-42-634-7443, or more look-a-like random numbers. This way it is harder for hackers to crack your software, it will never be impossible ofcourse.

4: crc32 are widely used.

Anyways, remember that there is always a way that people can crack everything. Make sure that you crypt the password and change this over time. It doesn't protect everything, but it is never bad.

My first thought is why do it this way at all? If you have Awesomium integrated already, use a secure https connection to setup the initial login. The fact that it is tcp should not degrade anything and in fact can help with getting initial nat traversals taken care of and other useful items. Doing the actual security over UDP to me is just risky, there are so many possible edge cases that making it solid is a fairly scary prospect no matter how much you think you have gotten them all.

Perhaps it wasn't clear above... the initial log in is already going through the HTTPS connection as you suggest. My questions were around handing off to the actual gameplay which currently runs over UDP.

I have just put together a little prototype version using TCP/TLS which was reasonably straight forward. I'll have a think about ditching UDP altogether and whether it will work for the game.

Wheter you need UDP depends on whether you must have information as fast as possible (not in terms of speed/bandwidth, this is identical -- but in terms of latency). If your information is "worthless" after some milliseconds so you could as well throw it away, it is better to use UDP. If a packet is lost, screw it.

Otherwise, TCP is what you want anyway. It works just as well and just as fast, and in some cases "better", but overall it's simpler on the library level. Yes, if packets are lost, TCP will resend and wait for them, but then again, this is just what you want.

About your questions on sequence numbers, not having sequence numbers start at zero has two advantages. First, if you encrypt the packet including sequence number, you do not introduce known plaintext, which is an attack vector. Second, if you leave the sequence number unencrypted, you have a much lower likelihood that someone is easily able to guess a believeable number and spoof it. So, in either way it's better not to start at zero, and it doesn't cost anything to do otherwise. Sequence numbers are more important for UDP as they are for TCP, since TCP already has its own sequence number implementation.

As for what hash to choose, in principle every simple hash that is inside the encrypted part would do. Or, of course, a MAC. Accidential bit flips due to "line noise" are very unlikely, they're discovered by two layers of hardware and protocol checksums and FEC.

The hash is there to ensure that no malicious tampering has happened. Which means that nobody who doesn't know the key must be able to generate (or verify) one. That's more or less what a MAC is, a key-dependent hash. Of course, you can just use a simple normal hash, append it to the other data, and encrypt everything. Now of course, the question is how long should such a hash be (like, 160 or 256 bits, or 512?). Since there'll be one hash in every packet, and since packets are short-lived, I'd say that 32 bits are plenty. As long as someone cannot successfully forge a packet that will pass by flipping bits in less than a few hundred milliseconds, there's not much they can do (apart from sending you with random garbage, but they can always do that).

I would call the game's pace moderate, it is not a twitch shooter so latency isn't the most critical. There is however quite a lot of information about the world flowing to the user which is updated regularly. I had gone with UDP so this information can be sent on an unreliable channel and dropped without issue.

After looking through all the details I think I'd rather have the simplicity of the secure TCP stream and I'll just have to be extra careful about not flooding it with too much information.

Thanks for your thoughts everyone smile.png

  1. Client connects to game server and transmits the auth ticket. Doesn't this already need to be encrypted to prevent against sniffing on open networks? But the server apparently doesn't retrieve the encryption key till later, so what is stopping someone else grabbing the ticket and connecting as you?
  2. Is there any reason not to start the sequence numbers at 0 given we already using a random encryption key?
  3. Aren't sequence numbers going to be fairly useless with UDP unreliable messages?
  4. What is a suitable hash for ongoing messages once authentication has been established? I assume this is really just a checksum to make sure the data hasn't been tempered with.

Also, would you still give the same advice today? Reading the archives here I get the feeling should really be looking at TCP/TLS instead, if at all possible...

1. Unless the player is playing the game on an open WiFi network, it is actually pretty hard to "tap into" the network connection and sniff the ticket. So, the question is whether your attack vector includes those with physical access to the networking infrastructure. If the ticket is tied to a remote IP address, then even stealing the ticket is not good enough unless you can also steal the IP address. It turns out that modern day use cases like the open WiFi on Starbucks and McDonald's and similar places allow you to do both, though (like Firesheep,) so using transport-level encryption is advisable if you want to protect against that.

2. If you have some other way to detect replay attacks or blind data injection, then the sequence number can start at 0 every time.

3. Sequence numbers are highly useful for UDP, because they let you know which of your packets make it through, and which ones don't.

4. If the goal of the "hash" is to protect against network-level hardware corruption, a crc32 would be fine. If the goal is to protect against a determined attacker with the capability of acting as a man-in-the-middle, then a HMAC with a shared secret and a strong algorithm is needed. A straight hash with a shared secret is not good enough, because the attacker can use hash data extension to inject his own data at the end of the message. HMAC fixes this. Btw; using md5 for the HMAC algorithm would let someone inject data as well, because creating 16 bytes at the end of a packet that achieves the HMAC you want with MD5 is no longer a computationally hard problem. I'd suggest sha-1 for now, for games, although if you're all security-minded, by all means use sha-256. And hope the NSA isn't interested in cheating in your game :-)

If you're using TCP, then TLS is a fine transport mechanism, but it does not solve the authentication problem by itself.

If you're prepared to set up unique client certificates for each client, you can do that, though. if you want to go that route, that would be a perfectly fine alternative, as far as I know. (I e: it's likely that self-signed self-issued certificates from an audited code base will be secure; it's also likely that pay-for CAs are by now not considered 100% secure.)

IIRC, you also can't really use UDP with the TLS stack as it uses full-stream in-order CBC.

All of which goes to the question: What kind of attacks are you *really* trying to defend against? If your game is a valuable target, it's much easier to install malware on your players' computer, than it is to do a cryptographic attack.

http://xkcd.com/538/

enum Bool { True, False, FileNotFound };

Can I turn the last question around and ask which types of attacks I should be defending against? Obviously the game is not a high profile target, but I do have concern for users basic privacy and safety. Is there a generally acceptable level of security (you could say standard or best practice) small indie developers should be aiming at? Some things like protecting passwords in storage and transmission are obviously in.

The open wifi issue has been kind of a big deal for us in the web space for a few years so it does interest me, perhaps it is less relevant to PC games where users are more likely to be at home though.

Let's say I go with the TCP/TLS option. I currently have the following basic prototype set up:

  • I create a self signed certificate.
  • The server has a copy of the public and private keys, the client has public key only.
  • TCP connection is established, SSL stream layered on top.
  • The server authenticates itself using the certificate.
  • The client checks that the provided certificate matches its copy of the public key, nothing else is accepted.
  • The client submits user name and password to the server which checks against its stored hash etc.
  • (login servers, tickets for easy reconnection etc have been ignored for simplicity).

At this point, can the server reasonably (* see below) just store the client ID against the open connection and assume it is good for the life of the connection? As far as I understand it the SSL stream has negotiated session keys, is doing the sequence, hash checks, encryption etc itself, and no one else should be able to interfere.

Thank you for your patience helping me think through all this. Most of my knowledge is about protecting web sites from SSL upwards, so I don't know what I'm doing at this level.

* Things that are explicitly outside of this projects scope:
  • NSA, or my local version the GCSB.
  • Criminals with drugs and wrenches.
  • Corrupt CAs, ISPs, network admins etc.
  • Client PCs with 10 IE toolbars installed.
  • Users with the password password.

This topic is closed to new replies.

Advertisement