How does storing hash passwords work?

At least part of the hashing must occur server-side. Indeed, whatever the client sends to the server grants access, so if that very same value is just stored as-is on the server, then you have the same issues as plaintext password storage, namely that a single read-only glimpse at your server database gives all the accesses.

In most cases all the hashing is done on the server, because the client is a Web browser than can, at best, do some Javascript, and Javascript is not very good at cryptographic tasks. The client sends the password itself (through HTTPS of course), and the server computes the hash, and compares it with the stored hash value. The attacker cannot simply "pass the hash"; he must send a value that, when hashed by the server, yields the stored hash value.

If the clients are good at making crypto (e.g. a heavy native client for a game), then the bulk of the hashing process can be done on the client side, but there still needs to be a final hash computation done on the server.


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 |   [email protected] |
+--------------+--------------------------------------------------------------+--------------------+

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:

  1. Login("markbuffalo", "$2a$08$BOHyNdXAVkWOUgPG/hvsjew7ZpngqJIgueY6m76xd4y3UllXUZLBy");
  2. 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.


To your first question:

It depends on the application. In most cases, the password is sent to the server in clear text (and this is insecure if the application uses an unencrypted protocol, such as HTTP). The server, however, might store a hashed value in the database. If it does, the application calculates the hash value of the clear text password received from the user and matches it against the value stored in the db.

To your second question:

The server expects a password input: X. The attacker somehow finds out h(X). Then if he would enter this hashed value to authenticate, the hash function would be ran once again against h(x), so he wouldn't be able to get in since h(x) does not match h(h(x)).

Tags:

Passwords

Hash