This is mostly a thought experiment for client/server communication, and I want to know the flaws.
When a user account is created (with
U as the username and
P as the password,) I generate a random salt (
S) and store these values in the database (on the server-side):
H which is computed as
H = hash(U+S+P).
When authenticating a user:
- The client first "attempts an authentication" by sending
Uto the server.
- The sever creates a challenge code (
R) for that user and stores it in the database, along with the time this challenge would expire (
T) and sends
Sback to the client.
- The client calculates its own version of
Ptogether; it has
P, and received
- The client then computes a new value
G = hash(H+R)and sends this
Gback to the server (actual "authentication request".)
- The server hashes its own
Gand authenticates the user iff its
Gmatches the value received from client and
Thas not passed yet.
- Whether authentication succeeds or fails,
Ris removed from the server database.
The flaws I'm already aware of are these:
- When creating an account, the password is transmitted in plaintext. This can be mitigated using a dedicated account-creation service that employs SSL for creating accounts.
- Attempting an authentication will result in some resources being allocated on the server. This can be mitigated by allowing only one (or a fixed number) of challenge codes per user.
- Trying to discover valid usernames and salts by generating many authentication attempts; which the server can foil by sending a random salt and challenge code even when the user does not exist.
Here are my specific questions:
- What am I missing? Where is the disaster waiting to happen?!
- What can I do to make this more secure/better? Other than using SSL and just sending plaintext passwords (plaintext only from the perspective of my application.)
- I'm thinking of SHA-256 for all hashes. Are there any flaws in the SHA-2 family? Is hashing the values once enough for generating
- For generating
G, are other permutations of values better? e.g.
- What conveniences am I denying my users? (Apart from their authentications now needing two round trips to the server.)
UPDATE: Ultimately, I want to do this:
After a successful authentication, the server generates a session key
K = hash(H+F) where
F is a random string.
F is sent to the user and
K is retained in the database. The client reconstructs
K using the received
F and encrypts all communications with the server henceforth with a symmetric cipher (e.g. AES) with
K as key.