How login password hashing is supposed to work
Note that in all snippets, I am intentionally excluding security features because I want to keep it compact. Do not rely on these code snippets for anything.
In usual network applications ,that employ password hashing, is the user password hashed on client side before sending it to the server or is it sent without hashing as encryption of plain text password to the server?
No, it's hashed server side. You send the password over plain text, or through HTTPS, to the server. The server will then generate a hash for the password, like so:
public void RegisterAccount(string username, string password, string email)
{
if (!Database.EmailExists(email) && !Database.UsernameExists(username))
{
Database.AddNewUser(username, Hash.GenerateHash(password), email);
}
}
Assume that Database.AddNewUser(string, string, string);
will insert the username, hashed password, and the email into the database. The hash is generated based on the password.
Let's say you put in "don't hack me please" as the password. bcrypt will turn that into $2a$08$BOHyNdXAVkWOUgPG/hvsjew7ZpngqJIgueY6m76xd4y3UllXUZLBy
, or some other variant based on the salt. The database now looks like this:
+--------------+--------------------------------------------------------------+--------------------+
| user | password | email |
+--------------+--------------------------------------------------------------+--------------------+
| markbuffalo | $2a$08$BOHyNdXAVkWOUgPG/hvsjew7ZpngqJIgueY6m76xd4y3UllXUZLBy | mark@buffalo.gov |
+--------------+--------------------------------------------------------------+--------------------+
Normally you're going to call the database to read the password for that username. Once you call the database, you're going to use a function, such as Hash.ValidateHash()
, to see if it matches. If it matches, you can log in using that non-hashed password.
Why can't you just pass the hash to log in?
You can in some very unfortunate cases.
The main question I have is if an attacker discovers the hashed passwords then why can't he just send hash to the server to authenticate in case of client side hashing?
This is possible, but it can only happen during severe instances of massively epic failure on behalf of the developers. In fact, I've seen this before, and I'll show you how this can happen via code:
public void Login(string username, string password)
{
if (Database.LoginMatches(username, password) ||
Database.LoginMatches(username, Hash.ValidateHash(password)))
{
logMeIn();
}
}
In the above snippet, the login wants to check to see if either the user has the hash, or the password which will validate into the hash stored in the database. This is not the correct way to do it. In fact, it's one of the worst instances of epic fail I've ever seen in my life.
With this code, you can simply enter the hash, or the password, and log in. Recall the previous table. Either of the following logins will work correctly:
Login("markbuffalo", "$2a$08$BOHyNdXAVkWOUgPG/hvsjew7ZpngqJIgueY6m76xd4y3UllXUZLBy");
Login("markbuffalo", @"don't hack me please");
That is the wrong approach entirely. It's not supposed to happen. At. All. Nevertheless, some developers have taken this approach for reasons I do not know.
What is the right approach to hash validation?
Like Rápli András said, h(x)
does not match h(h(x))
. The right approach is to check the incoming password against the hash to see if they match. If they do not match, do not use any OR
magic (||
); simply do not allow them to log in.