creating basic lobby services

Started by
18 comments, last by hplus0603 9 years, 3 months ago

hplus0603 - would you mind if I sent you a short private message? I believe I have worked out how I wish to do my authentication and logins etc and would like an expert opinion incase I missed something - but as silly as it sounds i'd rather there wasnt a thread bullet pointing exactly how my security works incase I go live and it's later found and gives someone with malicious intent a little edge :P

Advertisement

i'd rather there wasnt a thread bullet pointing exactly how my security works


I'm happy to review anything that is posted publicly in the forums. For questions where the forums do not get the benefit of the answer, I may be available for a paid consulting gig.

In general, your best bet is to go public with your questions. Your client is publicly available to anyone who plays your game, so analysis for a determined attacker is not hard.
And, for anyone to actually attack your game, the game has to be successful enough that a determined attacker thinks attacking your game is more interesting than attacking WoW / Clash of Clans / Battlefield / whatever.

If your security is good, then making it public is fine. If your security is bad, not making it public just means you have less chance of finding the badness.
enum Bool { True, False, FileNotFound };

Okay, well fair enough. It's just a "take every edge you can get" mentality in a world where malicious attempts on anything internet are inevitable :)

But for the benefit of the forums then, these are the steps i'm intending to take to authenticate and join a lobby. I really appreciate your feedback on it as although I have nearly two decades of programming behind me I do not have much experience with networking so this is a bit of a new avenue for me!

This is assuming a user account has already been created (probably through a web page) and there is an SQL db with the login name and a password hashed with a "server secret" to avoid ever storing plain text passwords.

client connects to login server
login server sends random challenge number to client
client computes hash of challenge combined with a user supplied password and also sends a login name
login server looks up client by login name, de-hashes stored password using "server secret" and rehashes the sent challenge + stored (now unhashed) password and compares to hash client sent.
if password okay, login server generates an authentication ID, stores it on the sql server and sends client a ticket with their auth id, their user id and IP of server to connect to (could do load balancing here by smartly choosing different server ips?)
client disconnects from login server and connects to new ip given for lobby server
lobby server asks for user id and authentication token
client sends requested information
lobby looks up user id, read authentication token stored by login server and confirms that users auth token matches and has not expired, sends an "all okay" to client.
Client may now perform lobby functions like send chat, receive chat, list games, etc.
Periodically lobby server should request auth token from client to ensure no "man in the middle" injection and everything is as should be - correct supply of token extends expirary on that token.
Any token that expires should auto boot the user attempting to use it out.
My main concern is whether just accepting commands from the client after an auth token has been confirmed as valid and then occasionally checking that whatever is talking still has the valid auth token is sufficient runtime security - the alternative seems to be prefixing every packet with the same auth token but it just seems like alot of extra data to be sending all the time?

client connects to login server
login server sends random challenge number to client
client computes hash of challenge combined with a user supplied password and also sends a login name
login server looks up client by login name, de-hashes stored password using "server secret" and rehashes the sent challenge + stored (now unhashed) password and compares to hash client sent.


This will work, although it requires that the server be able to decrypt the passwords.
That may be okay for a game, but is generally not a good idea, in case your server database gets somehow broken.
Making it hard for an attacker to get the actual password, such as using a specially hardened en/de-crypt server that's not visible to the external network, may help.
These days, I'd probably instead prefer to use TLS or HTTPS to provide plaintext name and password to the server, and the server hashes that plaintext password using bcrypt to compare to stored-in-database hashed-password. (This is safe as long as TLS / HTTPS is safe.)
An attacker can then only get the passwords that are in flight during the time of attack, rather than all the passwords.

if password okay, login server generates an authentication ID, stores it on the sql server and sends client a ticket with their auth id, their user id and IP of server to connect to (could do load balancing here by smartly choosing different server ips?)


I would probably send a ticket that consists of:
time:user-id:hmac(time+user-id,shared-server-secret)
This lets the lobby server verify the ticket (time+userid) without connecting back to the server, assuming lobby server and main server have the same clock and secret.

client disconnects from login server and connects to new ip given for lobby server
lobby server asks for user id and authentication token
client sends requested information


You can just have the client provide the information in its connection -- no need to "ask" for it. That's an efficiency thing, not a security thing.

Periodically lobby server should request auth token from client


Player could just provide the ticket every 30 seconds or whatever without any "request." You'd simply have a timer for how long ago a valid ticket was provided to the server, and disconnect any client that hasn't provided a valid ticket in X amount of time. Again, simplicity, but not a security thing.

My main concern is whether just accepting commands from the client after an auth token has been confirmed as valid and then occasionally checking that whatever is talking still has the valid auth token is sufficient runtime security


What attacks are you trying to guard against?

Most script kiddies on the internet (including game hackers) do not have the ability to receive data sent to a specific IP address. They can spoof the source IP address of some packet that's sent, but they won't see the return of that. Thus, once established, the periodic ticket sending is not particularly necessary.

Another option: You can provide an encryption key to the client when it first connects. The client can then encrypt each packet with that key. (Make sure to prefix each packet with a sufficiently long sufficiently random nonce!) The server can then tell that the client was the one that originally connected (or could originally receive the connect message) because the packet decrypts correctly.

Another option: You can use TLS. (Which means you can't do UDP NAT punch-through.)
enum Bool { True, False, FileNotFound };

hmm, the reason I went with that method (vs ssl etc) was after reading your chapter on networking and seeing Challenge Hash Authentication sold as a "major benefit" and indeed with no passwords being sent to sniff etc the ONLY downside I could see was the server storing plain text. I was not okay with the idea of plain text so my idea was that all passwords would be hashed server side by a secret key inside the server software itself so if someone DID get access to the db they would have hashed passwords and would need to then steal the actual software off my server and then reverse engineer it until they found the key.. or is there something much simpler they could do? I figured a large enough "secret" would prevent brute forcing, at least on any level anyone would bother here. Would you say this is not sufficient and you'd still go for TSL/HTTPS?

I really like your optimisation suggestions, obvious when read but probably wouldnt of got them any time soon by myself!

At the end of the day this is a multiplayer lobby. It does NOT handle any game logic at all - if a hacker got in the worst thing they could do is steal a users account, change their password and maybe delete their friends list or intentionally behave bad on lobby chat to try and get a user banned and invalidate their acheivements etc. I'm not sure how far I should take security - those things are certainly bad but not quite on the scale of "now I have your credit card information"!

edit: as I intend to run this from the azure cloud I believe I can trust microsoft with my data, even if I cancel my service later on :) there is also a default amount of redundancy using the cloud. but if I go the VM IaaS route as I intend then I guess the ultimate security of my server is down to my own configuration!

Would you say this is not sufficient and you'd still go for TSL/HTTPS?


With TLS/HTTPS, the hacker can get the password of one user by exploiting faults in the protocols (which have been more common than previously thought.)

With challenge signature, the hacker can't get the password that way, but can get the password of many players if they break your server (which also seems to happen more than previously thought.)

Previously, I used to think that a competent operations team could keep an encrypted password database from falling in the wrong hands, but these days, I'm just not sure --- both problems have happened with alarming frequency the last few years.
enum Bool { True, False, FileNotFound };

sorry to drag this thread up again but working on same project so didnt want to clutter board with more posts even though slightly different question now!

I have never used a database for such high volume data before, the biggest use i've ever put a db too is a forum. So atm i've been weighing up what data to store in ram and what data to store in DB, especially if I end up spreading the load over a few servers. I guess im "choosing my bottleneck", wether ram or db, and im really uncertain to what extent I can push a db!

tl;dr - should I store games players are hosting (and unhosting) in the database? or just user data (llogin/pw/email/stats/etc)? anything I should be aware of?

Thanks :)

I have never used a database for such high volume data before,


If I were to build it, I would store data that is going to be stale within an hour in RAM, and data that needs to be more persistent than that in a database.
I would also design my protocols to avoid changing persistent data frequently :-) Or, to put another way, any data that changes frequently, should be in RAM, and/or supplied on demand by the client.
enum Bool { True, False, FileNotFound };

thats really helpful! I'm thinking then that a "hosted game" could go in a database because it could last a rather long time depending on the game. However players may come and go from games quite frequently so perhaps each server should have in ram a list of online users (I have that atm) and what game they are in with the info for that game being in the database? Its tempting to put the users that are in the game in the db though because I could store it as a hostmask, userid@server so any lobby service looking at the users list could instantly see a) what users are in a game and b) what servers those users belong to so they could easilly send chat the right way without having to traverse a peer to peer tree (eg IRC). But that may be just too much hit on the DB when the userbase gets large.

So instead perhaps the db should store the game and who hosted it in the db, lobby servers can read that and know which server to inform when one of their clients requests to "join" and in response the server with the user who hosted can pass on a list of other users already joined and their host masks, that way user discover is one direct message and no server tree required!

So finally I guess - data that goes stale in an hour.. just to be clear do you mean data that wouldnt change or data that would cease to be useful? for example, a User name is rarely going to change. I could make my "online users list" just an array of database id's for looking up that stuff. However although the username wont change it will be frequently used / useful for lobby chatter lists, announcing joins and disconnects and all sorts of other stuff... so is that a candidate for ram or db lookups?

I guess I need to know if a db read is an expensive as a db write!

That would probably work just fine for may cases, especially when player joining/leaving is not frequent, and games have long life times -- chess, or whatever.

When I wrote a version of this, I put both "currently available games" and "players seeking games" in RAM only. This was more for a FPS-like experience, where games may be shorter, and player are at least semi-often looking for new matches.

A server that is currently accepting users would send an update with current game state (name/mode/current-players/open-slots/whatever) every 5 seconds or so. When the lobby server hasn't heard from a game hosting server for 12 seconds, it would drop that game from its list of available-to-match games.

Meanwhile, a player looking for games would send match parameters, and be sent back a list of games, typically once every 5 seconds as well. If you expect for there to be 100s of matched games in the response, you'll want to manage this with some kind of consistent sorting and pagination.

The only persistent data I'd store in a DB for a game matchmaker would be the user name and password hashes, to verify the user accounts (and perhaps something like a game purchase serial number or whatever.)
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement