Hash Based Authentication
This page is intended for fleshing out the details of a simple authentication scheme.
- The code that is already there, see Authentication.
- A hash function H() like MD5, taking arbitrary data as input and giving a fixed lenght output
- A (game) client
- A (game) server
- An authentication server
The authentication server holds a database of usernames and hash functions of their passwords.
An important sub-recipe is the scambling eggs-protocol, which is designed to permit an entity P to prove to an entitiy V that it knows a shared secret d without enabling an eavesdropper C to convince V. It works as follows:
- V generates a random bitstring s (the salt) and sends it to P
- P computes h0=H(d+s) and sends it to V
- V accepts iff h0=H(d+s)
where + denotes string concatenation.
On the Client
The client uses the existing feature of password based authentication:
- The server sends a password challenge, containing the username to authenticate and a random salt s generated on the server.
- The client retrieves the user's password (from the user or from storage in memory or the hard disk) p.
- The client calculates H(H(p)+s) and sends the result, together with the username, back to the server.
The real password, or anything useful to steal the user's identity, is not transmitted. If H() is a good trapdoor hash function, you can't reconstruct H(p) or even p even if you know s.
Simple Case: Game Server = Authentication Server
When a user is to be authenticated, the server generates a random salt s and sends that with the username to the client. It waits for the response with the client's version of H(H(p)+s). The server fetches the real H(p) from the database, calculates H(H(p)+s) on its own and compares. On match, the user is authenticated.
Game Server and Authentication Server are Separate
On the Authentication Server
The authentication server waits for connections from the game server. It reads a username to be authenticated from messages from the server, generates two random salts s1 and s2, fetches the hash of the user's password H(p) and calculates h1 = H(H(H(p)+s2)+s1). Yes, it scrambles the already scrambled password two more times. The result and the two salts are sent back to the server.
This communication is even more useless to intercept than the client-server communication, it's scrambled even more.
Another thing to note: the game server now does not have the possibility to use the authentication server's response to imposter the user on another server. The information is just good enough to verify the user's identity.
On the Game Server
When a user is to be authenticated, the server sends the username to the authentication server and waits for the response, consisting of h1, s1 and s2. It sends the username and s2 to the client as in the simple scheme, the client returns h2' = H(H(p')+s2) where p' is the password entered on the client. The server calculates h1' = H(h2'+s1) = H(H(H(p')+s2)+s1) and compares it with h1. If they match, p must be p' and the user is authenticated.
The Phishing Server
I can't set up a server to permanently steal identities, but I can do the following:
- I set up an innocently looking server and have a client in the background
- I wait for a victim to connect
- I connect my client to some other server that uses authentication, logging in as the same user
- I wait for the password query
- I forward it verbatim to my victim, he enters his password
- I forward the data sent back by the victim verbatim to the other server
- I'm logged in as my victim. Mwahaha!
Countermeasure: The salt s2, as the client receives it from the game server, always gets the game server's IP and port appended to it before it is used for scrambling. The authentication server does the same. Then, the phishing server would need to be able to forge IP traffic and appear at the other game server's IP.
Alternative: Three way communication. The salt s2 is not actually used by the game server, it could be sent directly from the authentication server to the client. In the above attempt, the message would go to the phishing server instead and the victim's client would not even trigger the password dialog.
Both methods don't work with current clients.
Faking the Authentication Server
If someone can forge the authentication server's response to the game server, he obviously can get free entry into the game server as anyone he wants. Countermeasure: Real cryptography could help here (we're not bound to the ingame network protocol here and could use ssl), but at least, the game server's message needs to also contain a randomly chosen token the authentication server's response has to mirror, or else the attacker can try to inject a forged response without being able to read the network traffic.
attacking the auth server
This is an online-protocol, i.e. the authentication server must be online in order to authenticate a player. It is therefore possible to prevent specific players from playing by bringing down their auth server. Countermeasure: the game server could fetch auth challenges from the auth server for later use and store them locally; short downtimes of the auth server will then only affect players visiting a game server for the first time, when no supply of challenges for them is ready yet.
Read-only access to a record in the authority's database is sufficient to impersonate that player. Of course, similar things are true for all authentication schemes.
Selecting Authentication Servers
Authentication servers will be identified by either a nickname or a full blown IP address/DNS name and port pair giving the exact connection data. Nicknames don't need to be unique globally, but obviously, it would be desireable if the auth server admins would at least attempt to avoid collisions.
Undoubtly, there would be several authentication servers available; a player should be allowed to have accounts on as many of them as he likes. Accounts will be identified like email addresses by the string <user name>@<auth nickname> or <user name>@<auth IP:port> or just <user name>. The client will have an input field where the user can enter all the accounts he is registered for. Current clients don't support that, they will either use chat commands like "/userid <user id>" or set their player nickname to a valid account identifier.
A game server has the following auth server lists:
- IP and port of a primary auth server. Account IDs without auth server part will be assumed to be accounts on this one.
- A nickname resolution list. A list of auth server nicknames and what IP and port they resolve to. Account IDs of the form <user name>@<auth nickname> will be resolved using this list. Auth servers on this list are automatically whitelisted and users authenticated by them are accepted, whether they are given with the nickame or full IP and port.
- A blacklist for auth servers that are not accepted as valid authorities, accounts on them will be ignored.
- A player banlist, listing the <user name>@<auth IP:port> usernames that are not allowed to join. Clients who have one of the banned IDs in their list of accounts will be banned.
- An auth server banlist, mostly for renegade auth servers that issue and infinite supply of player IDs. Clients who have one of the banned auth servers in their list of accounts will be banned.
A setting would determine whether users without account on an accepted auth server are allowed to play or not, default should be to allow play. Another setting would determine whether auth servers appearing in none of the lists should be accepted, default should be to reject them.
Authentication servers will send their nickname to the game server. The game server keeps a list of all the nicknames it has seen and avoids nick conflicts by modifying the given nicks, for example by appending a number. Nicknames listen in the nickname resolution list have precedence over all nicks in the automatically gathered list.
With the collected list, the server can assign each user account the human readable identifier <user name>@<authentication server nickname>.