Twitter  Facebook  Google+  YouTube  E-Mail  RSS
The One Man MMO Project
The story of a lone developer's quest to build an online world :: MMO programming, design, and industry commentary
Designing a Secure MMO Login System
By Robert Basler on 2010-07-28 17:42:48
Homepage: www.onemanmmo.com email:one at onemanmmo dot com

I needed an account system for my MMO but I couldn't find much info on designing such a beast. Having figured out much of this on my own, cobbled together from stuff I read, I thought I'd put together an article on a few of the things I learned while building my login server, LoginD.

Disclaimer: I am not a security expert, so this system undoubtedly contains flaws. Take the information here as a starting point for your own design, or better yet, hire an expert.

Account Names


A lot of systems use email addresses as accounts for logging in. There is one big problem with this practice -- it is really easy to guess a user's account name. Any thief is already half way to being logged in. So for LoginD, I'm using account names, distinct from the user's email address. As a practical matter, account names should have minimum and maximum lengths, be stored as unicode for your international customers, and disallow whitespace for easier handling.

You also should not use account names for forums, chat or guild identities in-game. Have the user create another identity to use in-game or in public.

Passwords


There are a lot of options for passwords. Length is a big concern, as it pertains to guessability. Passwords should have a minimum length, depending on how secure you want your system to be. I think 6 characters is an absolute minimum. Passwords should be case-sensitive, allow upper and lower case, numbers, symbols, punctuation, anything the user can use to make their password unique.

You probably want to disallow passwords that feature all the same character AAAA, or simple sequences like 123456, ABCDEF, QWERTY and really common passwords like GOD or PASSWORD. You might also consider checking passwords against a password list - a Google search for "password cracker word list" gave me a number of good lists to start with.

The issue with restricting passwords is that you can make it too hard for the user to create a valid password. There's a balancing act between easy-to-remember, and easy-to-guess passwords. Some systems use automatic password generators, but the passwords they create are impossible to remember and users hate them.

I'll probably go with a system that tells the user how secure or insecure their password is when the account is created. Google has a password strength checking API you can use. It returns a value between 1 (weak) and 4 (strong.)

Microsoft had an interesting idea for limiting passwords. Rather than checking potential passwords against a dictionary, they check them against a list of existing passwords in the system. Once a password is used N times (where N is small), it can't be used again and the user needs to use another password. This gives a more even distribution of passwords and prevents too many accounts from being compromised by a single easy-to-guess password.

Login Algorithm


I wasn't able to find a whole lot of information on login algorithms, but there were some basics I was able to gather. The first rule is to never send login information unencrypted over the internet. The second, is never roll your own encryption.

Hashes


To avoid sending unencrypted login information, my MMO client hashes both the account name, and the password, using a SHA hash. A hash is a mathematical formula that converts a message into a fixed-length string of digits known as "message digest" that uniquely represents the original message. A hash is a one-way function - that is, it is infeasible to reverse the process to determine the original password. Also, a hash function will not produce the same message digest from two different inputs.

Once the account name and password are hashed, the client sends the two hash results to the server to compare with existing accounts. Hey, we're sending data securely!

Whoops. There is a huge problem with this approach -- if someone captures your login information, they can easily resend the same packet later to log in. This is called a replay attack.

Salt


The solution to this replay issue is salt. Salt is a unique bit of information sent by the server to the client which the client then incorporates into the hash calculations. The Salt should be random -- cryptographically random, not stdc random. (If an attacker can guess your random numbers, it compromises your system.) Putting the salt into the hash calculation is a simple matter of putting it into block of data that is hashed. Since both the client and the server know the salt value, the server can easily compare hash values to determine if the login is valid.

Server Security


You probably want to consider protecting the login information stored on the server against possible attacks from inside your network. The most common way to do this is to hash the password in the server database. To do this and still have the added protection of salt, the server hashes the hashed data retrieved from the database a second time with the salt. The client does the same thing: it hashes the plain text password, adds the salt to the hash result, then hashes again.

Since we don't want the account name to be transmitted in plain text over the internet, we need to use a hash of the account name as a database key so we can look up the correct account using the login data from the client. Since we are storing hashes of the account names, when accounts are created, we must check that the new account being created doesn't have a hash collision with an existing account. Hash algorithms are designed to minimize collisions, but it is still a remote possibility that needs to be addressed. The simple thing to do is in case of a collision, require the user to choose a different account name.

Detecting and Handling Brute-Force Login Attempts


The server maintains a blacklist of IP's with failed logins, independent of login ID. Too many failed logins from a single IP, and that IP gets ignored from then on. If the number of failed attempts for a single IP, or the number of IP's gets too large, Ops gets paged. The list of blacklisted IP's can be shared with the frontend load-balancer to redirect attackers to honeypot machines and keep the regular servers relatively unmolested for real users.

Mitigating Denial of Service Attacks


Denial of Service (DOS) attacks are getting more common. The server maintains a list of active login sessions. The problem comes if there are many, many clients attempting to log in since the server could theoretically run out of memory. To help fight this possibility, sessions are tagged with time values so older sessions can be automatically expired.

Locking Out Accounts


One simple way to slow down brute-force attacks is to limit the number of failed attempts and then lock the account from further attempts. The danger here is that if the limit is too low, you will lock out your fumble-fingered customers and get support calls. Doing some research on the web, I found a study that concluded that locking out after ten attempts gives the best balance of protection and customer convenience. I'm not a big fan of any feature that requires a human interaction to address it, so LoginD reactivates a locked account automatically after a short period of time.

Timing Attacks


This was an interesting discovery. Typically strings are compared with strcmp which exits as soon as it finds a character which doesn't match. Someone who knows this can attempt to gain access to an account by logging in repeatedly, changing the password hash one character at a time, measuring the amount of time it takes for the login to fail. A, B, C, D, aha! It took .15ms longer to fail that time, first character is a D. DA, DB, DC, DD, DE, DF, aha! And so on. Of course it isn't quite that simple, an actual attacker needs to compensate for network variability in packet timing, but with enough attempts, it is doable. Fortunately there is a simple fix, do the hash string compare yourself and don't exit early on a mismatch.

PIN Client Security


A feature I'm considering adding is a PIN system which is activated whenever a new computer attempts to log into a given account. This prevents attackers from compromising an account by using a keylogger on the client machine.

To implement this feature, each time someone attempts to log in, they also include a hash of some information unique to a given client PC such as the hard disk serial number, network card MAC address, CPU serial number, etc. This gives each PC a unique identity we can tie an account to.

If the login server hasn't seen that hardware hash previously, it requires the entry of a PIN (typically numeric) to validate that the login is legitimate. Since users will typically log in from the same machine, they won't usually have to enter the PIN, so it is less likely to be caught by keyloggers.

The trick with the PIN is that you must only use it for logins from new machines, and make that very clear to users that that is the only situation that the PIN is used. That should protect users from bad guys phishing for their PINs.

The Login Algorithm


The login algorithm as it is currently implemented is:


Client: Send Hash( Account Name ) -> Server.
Server: Generate cryptographic Salt value.
Store Salt in session information along with session start time, IP address of client, Hash( Account Name ).
Send Salt -> Client.
Client: Send Hash( Account Name ) + Hash( Salt + Hash( Password ) ) -> Server
Server: Lookup session using Hash( Account Name )
Verify Session information not too old, all session info matches.
Lookup account in database using Hash( Account Name ) -- do this as late as possible so DOS attacks cause less work on the server.
Hash( Session.Salt + Database.HashedPassword )
Compare to client's hashed and salted password and send pass/fail to client.


The Login Database minimally contains these fields (plus others of your choosing):


Account Name
Hash( Account Name ) - Must be unique
Hash( Password )


I should note that when I say Database, I don't mean SQL database. You don't want to be hitting a SQL database for every login attempt. LoginD loads the entire account database into RAM from the SQL server at startup. It propogates changes to the database back to the SQL server in periodic batches to minimize SQL database load.

Crypto Info


If you want to learn more about cryptography, I highly recommend reading Applied Cryptography by Bruce Schneier. The book is easy to read and full of helpful algorithms.

Once you are familiar with hashes and cryptograpically secure random number generators, there are a number of free crypto libraries you can use, a couple good ones are OpenSSL and Crypto++.

By Robert Basler on 2010-12-16 10:48:15
Homepage: www.onemanmmo.com email:one at onemanmmo dot com
And to prove I'm not an expert, read this helpful post from an actual expert at Matsano Security LLC.

(This is a response to this which is also very illuminating.

Added some todo's to the logind server.
By omeg on 2012-02-28 04:22:53
Homepage: omeg.pl email:
I'm using Secure Remote Password for my login server. It's a zero-knowledge password proof algorithm and has the nice property of generating a session key at the end of authentication that can be used to encrypt all further communication.
By GeorgeR on 2012-06-12 07:20:53
Homepage: email:Dukecg at gmail dot com
By Robert Basler on 2012-06-12 21:28:40
Homepage: www.onemanmmo.com email:one at onemanmmo dot com
Thanks for the link, it points out a couple of additional security concerns that I was not originally concerned with:

1) Using symmetric cipher to secure data in transit against packet sniffing. I'm not really too worried about this, since the client can decrypt the data anyway. It is potentially more useful as a tool to slow someone down who is using a packet analyzer on your game.

2) Using symmetric cipher to guarantee that the client who authenticated is the client who is sending packets to the server. This is more interesting from an authentication point of view. I'm not sure how likely it is that other players will try to hijack someone's connection (given that the server does not give any player the IP of another player.) I suppose if someone was sniffing a cable-modem network they might practially be able to hijack another player's session.

It would be easy enough to tack on a key exchange phase using RSA public key encryption to exchange keys for a symmetric algorithm.

One game I know uses blowfish (and he recommended the encrypted session as a tool against packet sniffers.) If AES performs as well as the article indicates, it would work as well. I have CryptoPP integrated, so there are lots of algorithms to choose from.
By L on 2014-08-30 12:11:57
Homepage: email:
hi, im new to all this ... what is "salt" ?
By Robert Basler on 2014-08-30 12:19:14
Homepage: onemanmmo.com email:one at onemanmmo dot com
Its just a random number appended to the data you are hashing so that if you hash the same password for two different users, they don't have the same hash. That way it is harder for someone with a list of common passwords to hash and compare them to the passwords in your database.

New Comment

Cookie Warning

We were unable to retrieve our session cookie from your web browser. If pressing F5 once to reload this page does not get rid of this message, please read this to learn more.

You will not be able to post until you resolve this problem.

Comment (You can use HTML, but please double-check web link URLs and HTML tags!)
Your Name
Homepage (optional, don't include http://)
Email (optional, but automatically spam protected so please do)
What color is a lime? (What's this?)

  Admin Log In



[The Imperial Realm :: Miranda] [Blog] [Gallery] [About]
Terms Of Use & Privacy Policy