This is going to get long, so prepare. Basis of the question is, Do all these steps improve security, or am I completely overthinking the problem? Are my assumptions/thought process valid?
We all know there are many types of attacks a hacker can use to infiltrate a system, however, this deals with password hashing, so I am going to ignore other attacks like injections, etc. As far as I'm aware, there are 2 types of attacks that good password hashing can help prevent: offline brute-force attack and offline rainbow table attacks. In order to perform these, however, the attacker must first obtain access to the user-password file, where the passwords are stored as hashed versions, not plain text. A brute force attack involves guessing the user's password many, many, many times until the password he guessed matches the hashed password in the file. If you hash chain, the attacker must keep guessing down the chain, checking against the hashed version at that level until he arrives at the original password. Ranbow attacks are similar, but instead of guessing, the attacker uses a lookup table of pre-computed hashes based on commonly used passwords, words, and common variations. Rainbow attacks can be thwarted by using uncommon passwords or salts (password prefixes) which would not be contained in a pre-computed table. Instead, the attacker would have to compute this table on the fly which takes much, much longer. We can never prevent the attacker from guessing the password and checking to see if it is correct, but we can make it harder on him by making him guess many more times. We do this by chaining hashes so that whenever the attacker cracks the i'th hash, he must then repeat the process for the (i-1)th hash. However, if the attacker knows how many times we hash the password, this is mostly moot (see NOTE2). Anyway, on to the code:
function createHashedPassword(char[] username, char[] userPassword)
{
char[] secretCompanyString = "Some Secret Company String Only Visible via Source Code";
char[] companySalt = hash_sha512(secretCompanyString+username); //NOTE1
memset(secretCompanyString[0], 0, sizeof(secretCompanyString)); //remove secretCompanyString from memory
char[] userSalt = salt_generator_512();
char[] hashedPassword = userSalt + hash_sha512(companySalt+userSalt+userPassword);
memset(userPassword[0], 0, sizeof(userPassword)); //remove userPassword from memory
int NumIterations = 4242+userPassword.length; //DOES MAKING THIS NUMBER PRIVATE/PASSWORD-DEPENDANT ENHANCE SECURITY? SEE NOTE2
for(i=1;i<NumIterations;i++) //NOTE3
{
userSalt = salt_generator_512() + userSalt; //SHOULD I BE USING A NEW SALT EACH TIME? SEE NOTE4
hashedPassword=userSalt+"$"+hash_sha512(companySalt+userSalt+hashedPassword);
}
numIterations=0; //Remove number of iterations from memory
memset(companySalt[0], 0, sizeof(companySalt)); //remove companySalt from memory
return "$6$"+hashedPassword;
}
Note1: I believe that this is better than companySalt=secretCompanyString
, because if a user can brute force his way into the top level of the chain (i.e. finding that
companySalt+userSalt+hashedPassword
leads to hashedPassword
), he would see that it was in the form of companySalt+userSalt+hashedPassword
, and
the companySalt
would no longer be secret. However, being user dependant, he would not be able to use it to help crack other passwords. He could then attempt to brute force
his way into figuring out that the companySalt
=> secretCompanyString+username
, but that would involve yet another (long) brute-force attack.
NOTE2: I believe making the number of iterations private would help a great deal because if the attacker knew how many times, all he would need to do is guess a password, hash it
N times, and then check the result against the hash, instead of having to crack the umpteenth hash, and then have to work his way down the chain until he arrives at the password.
If this value was public, I don't believe it would be any more secure to hash chain than it would be to only hash the password once. Am I right in thinking this? In terms of the
second point, making the number of iterations password dependent should enhance security against brute-force attacks. Assume a user of the site gains access to
the password file. He knows his username, password, and public salt. He can then brute force his way into getting the companySalt
and number of iterations (assuming he knows
that we use a company salt and chain hashing scheme). With that information, it would be easier, but still not easy, to then brute-force his way into another user's
account. However, if the companySalt
and/or number of iterations changes from user to user, this information would gain him nothing. Making it private/user dependant, I believe,
should help prevent brute force attacks, not rainbow table attacks. This leads me into NOTE3.
NOTE3: I know we are supposed to hash chain, but why does this increase security? To slow down brute-force attacks by a factor of N? I'd assume it would also make using Rainbow tables infeasible because the rainbow table would have to contain all N entries, which would be practically impossible / impractically large.
NOTE4: After the attacker figures out we are using a company salt and brute forces his way into his first level, he would have built a huge table with guesses in the form of
"companySalt+userSalt+XXXXXXX"
. If we used the same salt over and over, there is a possibility (however small) that in the process of guessing one link in the chain, he
he already calculated a hash for another level. When he gets to that level, he can simply use his lookup table. But by pre-appending a new salt to the old salt each time,
it breaks that pattern and makes it impossible for him to have already guessed that hash. He will have to start building the table from scratch. I am pre-appending here so that
I can keep track of the hashes I used to make password checks possible.
I am expecting to get a bunch of answers discussing the inefficiency of the hashing algorithm (how long it takes, how much memory it will take to store the hashes, etc), but assuming I have a supercomputer w/ 800 petabytes of storage, and all I care about is maximum security, would all these steps help?