I'm not sure if you mistakenly mixed encryption and hashing. If the user's password is encrypted and not hashed, then the attacker can steal the entire user password in case of data violation.
There are a number of factors that you seem to be looking into when it comes to authentication. Firstly, any hashing should be done in the back-end and never in the interface. Interface hashing still leaves you vulnerable to hash attacks.
Some developers use a double hash approach in which they enter the password in the interface and then reuse it in the background. I believe this is not necessary, the front-end password should be covered by the HTTPS ( TLS ) layer, however this can be discussed.
First, let's clarify two key terms before explaining how to safely store and authenticate users.
Encryption
You indicate that user passwords are encrypted, not hashed. What encryption functions are matching input (user password) with output (encrypted password) using the 1 to 1 method, which means that it is reversible .
This means that if a hacker gains access to the encryption key (private key), they can easily cancel the whole process.
hashing
Instead, the user password should be hashed on the server side. What for? Because you can get away from comparing the two hashes to see if they match even without saving the textual representation of this value.
And again you may ask: “Why?” Good, because the hashing functions are one-way, which means that the value of plain text cannot be undone (well, that’s very complicated), I won’t go into details.
What should I do?
User passwords should never be stored as plain text in any part of the web server. Instead, you should store the user hash. When a user tries to log in, you securely receive your plaintext password through HTTPS / TLS, a hash, and if both hashes match, authenticate the user.
So the database table might look like this:
+--------------------------------------+ | ID | Username | Password Hash | +--------------------------------------+ | 1 | foo | $2a$04$/JicM | | 2 | bar | $2a$04$cxZWT | +--------------------------------------+
- Note that hashes are truncated by 4-round BCrypt hashing (AKA - Invalid)
Now let's take an example, between Alice and our server. Do not take data literally.
Alice sends a login request with her credentials, which first goes through our secure transport layer:
{username: "foo", password: "bar"} -> TLS() -> ZwUlLviJjtCgc1B4DlFnK -> | Server |
Our server receives this, then uses its certificate key to decrypt this:
ZwUlLviJjtCgc1B4DlFnK -> KEY() -> {username: "foo", password: "bar"} -> Web Application
Excellent! Our powers passed safely, now what? Hash this password and compare with what we got in our database.
BCRYPT('bar') -> $2a$04$/JicM
if ($2a$04$/JicM == user.get_password_hash) { authenticate(); } else { return status_code(401); }
Now we were able to authenticate the user by storing the irreversible hash value and never storing the plaintext value. This should answer your first and second question.