4

I was reading this article about hashing passwords when I came to this part:

To Validate a Password

  1. Retrieve the user's salt and hash from the database.
  2. Prepend the salt to the given password and hash it using the same hash function.
  3. Compare the hash of the given password with the hash from the database. If they match, the password is correct. Otherwise, the password is incorrect.

But I am a little confused with the flow this would follow, for example lets assume I have a database with a user table with id,name,password and email and in order to login to some app I need to input my email and password.

Following the the steps above, I first need to get the salt+hashed password of said user stored in the database.

Question:

Assuming I am using a simple stored procedure would the only way be to do it like this...

    CREATE PROCEDURE [dbo].[sp_validate_user]

@us_email VARCHAR (MAX)
AS
BEGIN
 -- SET NOCOUNT ON added to prevent extra result sets from
 -- interfering with SELECT statements.
 SET NOCOUNT ON;

    -- Insert statements for procedure here

SELECT  us_id,
        us_name,
        us_pass,
        us_email


 FROM  Users
 WHERE us_email = @us_email

END

Then following step two and three:

    public static bool ValidatePassword(string inputPassword, string storedPassword)
    {
        // Extract the parameters from the hash
        char[] delimiter = { ':' };
        string[] split = storedPassword.Split(delimiter);
        int iterations = Int32.Parse(split[ITERATION_INDEX]);
        byte[] salt = Convert.FromBase64String(split[SALT_INDEX]);
        byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]);

        byte[] testHash = PBKDF2(inputPassword, salt, iterations, hash.Length);
        return SlowEquals(hash, testHash);
    }

My concern comes from the fact that if I am creating objects with the data pulled from the table, doesn't that make the information within vulnerable somehow?

Also does that mean that the only way to use this validation is pulling all the user's information based only on a username/email just to check in runtime if the input password and the hashed one match and then letting said user access the information?

I'm sorry if this sounds confusing but any insight would be great.

Code Grasshopper
  • 610
  • 1
  • 11
  • 29
  • Not sure why you are splitting the stored password. It should just be a hash. The salt is added to the password prior to the hash function, not after. – codenheim Aug 28 '15 at 04:56

3 Answers3

7

It looks like you may be thinking of it backwards. The salt is added to the cleartext password before passing to the hash function. Store the end result in the database.

Commonly, the salt is the username. Something unique to each user to thwart dictionary attacks. (A dictionary attack relies on the economy of scale by cracking one password and then looking for other instances of the same crypto-text. It used to work especially well on very large user databases like well known sites that have millions of users, but hopefully those sites use proper salting and key derivation nowadays).

So for username u, password p, assume SHA2 is hash function. Concatenate u + p to get a salted value, then hash it.

hashtext = SHA2(u + p)     // in this case, + is concatenate

hashtext is what you store in the database.

For the login, user enters his username u2 and password p2:

tryhash = SHA2(u2 + p2)

Query database for a user record matching u2, with password hashtext of tryhash

Lets say you have an MVC action receiving loginViewModel which is populated with cleartext email or username as well as cleartext password, entered from the page:

var loginUser = new User(loginViewModel);
CalcHash(loginUser);

var realUser = users.Find(loginUser.username);
if(realUser.HashPassword == loginUser.HashPassword)
    // success

While it is also possible to add the hashed password as a second argument to your Data Access method, ie. users.Find(username, hashPass), it is usually not done this way, because you need to access the user record even if the password fails, in order to increment password failure count and lockout the account.

codenheim
  • 20,467
  • 1
  • 59
  • 80
  • Would accesing the records even if it fails constitute too much of a risk? I'm talking about a very small, home-ish/pet project application. Either way rather do things right. – Code Grasshopper Aug 28 '15 at 05:14
  • No, unless you let information leak back to the user. As long as your error is the same, regardless of the reason for failure, then the attacker cannot glean any information from the attempt. Though many systems don't follow this rule and allow you to find out whether a user or email exists within the system. Its a classic reason that sensitive sites should never require email address as a username, people can find out whether you belong to that site if they know your email. – codenheim Aug 28 '15 at 05:22
  • For example, look at the builtin login code for an MVC ASP.NET project, or a sample Membership or Identity implementation. They all access the user record in order to update certain stats (passwordFailures++, etc.) – codenheim Aug 28 '15 at 05:23
  • The salt should be random and of a certain length, and not derrived from other information like the username. The salt is not the only information you have to store with the hash (cost factor and algorithm), so you have to query the database anyway. – martinstoeckli Aug 28 '15 at 18:37
  • @martinstoeckli - Since the salt is to "randomize" the resulting hash product against dictionary & rainbow attacks, requiring access to the password database, please explain the advantage of generating a random salt vs. using an existing unique field like username or email? I can understand the need to use a longer salt, that is addressed with password derivation functions, but why do you discourage using an existing field? Are there known attacks that I am unaware of? (I admit my knowledge may not be current, so my question is sincere, but my opinion is the username is ok). – codenheim Aug 28 '15 at 18:55
  • The purpose of the salt is, to prevent an attacker to build a single rainbow-table to get all passwords at once. If you e.g. use the username as salt it could be 1) too short (the username `bill` would enlarge the rainbow-table only by 4 characters), 2) well known (rainbow-tables can be built for user `admin`), 3) predictable (one could precalculate the rainbow-tables for certain names/emails to use them on several sites). There are other attacks thinkable, but actually we should ask the other way round: why not do the best we can and use a random salt? – martinstoeckli Aug 28 '15 at 19:12
  • @martinstoeckli - Thanks, I admittedly did not know the extent of the Rainbow Attack's capability (I'd heard of it, but thought decent salting with email or username via key derivation was enough), and now that I've read more on it, I see what you are saying. Thanks for the explanation. No disagreements on "why not", I just wanted to know the reasoning. So wouldn't it be nearly as effective to use key derivation passes on the salt or the salt + password to increase the length of the password prior to hashing? – codenheim Aug 28 '15 at 19:59
  • 2
    Well if i understand you correctly, you mean to hash the username and use it as salt? This would at least mitigate the problem 1 with the short salt, the other problems are not solved though, because it is still predictable. In practise this may not have a big impact, but then i do not know all possible attacks myself. The best is still using a random salt and because of the other parameters like the cost factor there is no advantage in using a derrived salt (you need the cost factor and the algorithm as well to verify the password). – martinstoeckli Aug 28 '15 at 20:56
1

The article covers ASP.NET (C#) Password Hashing Code but you seem to want to use a database?

You have three things to worry about; the unique key for the user (username), your chosen hashing algorithm and adding a salt to the password attempt (prevents rainbow table attacks).

To validate a password you should create a sql stored procedure that accepts the username and password attempt as parameters. This data is in plain text and has been entered into the web form, passed to the web server and will be passed into the database server via the stored procedure.

The stored procedure will do the following;

  1. Lookup the data row for user based on matching the username parameter with the username field and select the stored salt field

  2. Append the salt from (1) to the password parameter and hash the result

  3. Lookup the data row for the user based on matching the username parameter with the username field and the hash result from (2) with the hashed password field.

  4. If there is no row found the password hashes don't match and are wrong so return a suitable error code

  5. If there is a row found return the useful user data i.e. First Name, Address

If the stored procedure handles all this then the web server never needs to know what the salt is or the hashing algorithm. At no point does the hash result or the salt get transmitted out of the database server.

Dave Anderson
  • 11,836
  • 3
  • 58
  • 79
0

I think you understood it correctly, this is the usual workflow:

  1. Get the password-hash by username SELECT password_hash FROM user WHERE email=?.
  2. Extract the salt from the password_hash, or get the salt from a separate field.
  3. Calculate the hash of the entered password with the extracted salt and compare the hashes.

Validating the password cannot be done in a single query, because you first have to extract the salt. Appropriate hash functions like PBKDF2, BCrypt or SCrypt are often not supported by the database system, so you have to do the validation in your code. Additionally to the salt you also have to store other parameters like the cost factor and the algorithm (to be future-proof), so it is a good idea to store all these parameters in the same database field.

The salt should be a random string of at least 20 characters, so it is not safe to use the username as salt, or to derrive the salt from other information.

martinstoeckli
  • 23,430
  • 6
  • 56
  • 87
  • In point 2, how do you propose to extract the salt from the password hash? – Starjumper Tech SL Jun 23 '19 at 06:21
  • @System24Tech - Most password hash functions do it on their own, they usually have one function for hashing `password_hash()` and another function for verification `password_verify()`, the latter will extract the salt from the hash string. A common format is explained in this [answer](https://stackoverflow.com/a/20399775/575765). – martinstoeckli Jun 23 '19 at 09:43