A. Your Salt should ALWAYS include a feature you have to store anyway about the user that would be unique to your service. Use multiple if you need to. This applies no matter what Hash mechanism you use. Also include a calculated value that isn't stored.
Example:
UserName + PassWord + User ID + CreationDate + <Numerals from a Base64 encode of the users first and last name>
Don't put all of this in the same database, so that a Data Breach won't give an attacker all the data they need.
No Rainbows for that combination. If a user changes their user name update their hash.
This is why in good services you have to provide your password when changing your username even when you are logged in.
B. To Validate the User Name:
Step 1 is always "Check that the password meets your password Criteria. If you have a requirement that passwords are 6-24 characters, contain 1 upper, one lower, and one digit, then make sure the password has those before you do anything else. No database lookups for bad passwords. No running bcrypt on a 4 megabyte section of post data. This is how you keep Denial of service attacks from crushing your service.
C. MD5 and Sha1 are only bad because there are rainbows for them, and because you can calculate every possible combination of values for a user/pass quickly if you know a single user/pass in the system you can calculate the salt methodology if you have access to the source code as well.
I won't tell you to use either of these, but if you lose your source code, and two databases worth of data so they have user, pass, and user data, you did something else horribly wrong, or it was an inside job and nothing would have saved you. (bcrypt is expensive if you have millions of users a day logging in and not any more secure if you did the other things above.)
No sane system follows the "store the data in multiple places" design; you'd need to pull it all back together to validate the password anyway, and a breach is extremely likely to compromise a system that can do exactly that.
Similarly, creating a salt from a calculation rather than, say, taking 128 random bits, is not going to help, nor is depending on the secrecy of the source code ever a prudent choice. It will certainly produce less entropy than the latter, will not produce a different salt across password changes, and is just very, very hacky.
I agree with sk5t. You can't count on the source to stay secure in a breach. It only adds unnecessary complexity without adding security. You are simply adding length to the password with your own key lengthening algorithm using values that are still in the database or source. You might think your clever (UserName + PassWord + User ID + CreationDate + ...) function adds strength because the current cracking tool doesn't have your clever function. This clever function can be easily added to the cracking tools thus rendering your clever function useless. What you failed to do was actually add a secret that you can guarantee to keep secret even when the database and source are breached. This is why a secure hardware device like the YubiHSM is recommended as the secret is never revealed even in a breach.
A calculated value means you need source to figure it out. Only PHP and JS/Node scripters assume that their source isn't safe in an attack. The rest of us assume that our compiled code is safe.
(Over generalization, but if people can get your source code basically there is zero you can do to keep them out)
Storing data in more than one store is a best practice.
Your random 128 bits have to be stored somewhere. That means an attacker needs only the database, not the source. In any large scale environment you likely have multiple machines. When a machine gets taken out of service at your hosting provider your database might still be on it when it hits the dumpster. If your source code is on another machine it doesn't matter.
The same is true of the Private User Data and the passwords. two locations means you need two breaches to do anything.
>A calculated value means you need source to figure it out.
As I mentioned in my other post, it really doesn't. Using a calculated value means that the entropy of your salt is only as high as the values used to calculate it.
>Only PHP and JS/Node scripters assume that their source isn't safe in an attack.
So, the vast majority of websites.
>The rest of us assume that our compiled code is safe.
Why would you assume that?
>if people can get your source code basically there is zero you can do to keep them out
If people have your source code, and your passwords are stored properly, they aren't going to have any easier of a time cracking them.
>Your random 128 bits have to be stored somewhere. That means an attacker needs only the database, not the source.
The whole point of a salt is that it doesn't have to be secret.
The cracking community, unlike the bulk of the mainstream programming community, has not lost the art of disassembling executable code. Hiding source code provides negligible additional security.
It still confers a negligible advantage. If the attackers have gotten into your data server, why shouldn't they be able to get into your application server just as easily? All you're doing is slightly inconveniencing the attacker before they can derive your salt wholesale.
Always remember: a secure system is secure even if the attacker can see your source code. Any work you do based on assuming the opposite is a waste of time.
Really? How about because my database server could be something like DynamoDB that I don't own. Why would the execution and the Data server have the same vulnerabilities. The whole reason to have two servers is so that you can say. "The only thing that can talk to the database is a server on the 'Inside'", but injection attacks mean you could possibly exploit the data server with no access to the execution server.
Even so, you're proposing a very low entropy salt. If the attacker can crack one or two weak passwords, they can start limiting their search space immediately, and crack more passwords. As they gain more examples to work with, they will be able to work out how your salt is derived, and then they can start looking for salt collisions based only on the data that is stored in the clear.
At that point, your attacker has not only the ability to derive the salt and attack each password individually, but potentially the ability to generate rainbow tables for subsets of users with identical (or largely identical) derived salts.
The point is that at best, you're adding a term to the overall hardness of the attack. When you use something like bcrypt, you are multiplying the hardness of the attack. There is no comparison between what you've proposed with MD5 or SHA-1 and simply using bcrypt on a random salt, even if the latter has your source code in the latter but not the former.
>Your Salt should ALWAYS include a feature you have to store anyway about the user that would be unique to your service.
What is your rationale for this? Is your concern is that a random salt will accidentally collide? With a 128 bit salt, there is a one in a million chance of a collision occurring in a set of 26 quadrillion.
>UserName + PassWord + User ID + CreationDate + <Numerals from a Base64 encode of the users first and last name>
That is just incredibly low entropy compared to a random string of bits. If you do this and use (as you suggest is just fine further down) MD5 or SHA1, you are begging for disaster if your site is worth attacking.
>MD5 and Sha1 are only bad because there are rainbows for them
Wrong. MD5 and SHA-1 both have significant vulnerabilities.
>bcrypt is ... not any more secure if you did the other things above
Insanely, completely, ridiculously wrong. MD5 and SHA-1 are stupid fast. A GPU-based cracker can try 5.6 billion MD5 hashes per second, and 2.3 billion SHA-1 hashes per second. If you're using a long random salt, you might hold out for a while after a data breach before all of your passwords are cracked. If you derive a low-entropy salt the way you explained above, you'll be completely boned.
A. Your Salt should ALWAYS include a feature you have to store anyway about the user that would be unique to your service. Use multiple if you need to. This applies no matter what Hash mechanism you use. Also include a calculated value that isn't stored.
Example:
UserName + PassWord + User ID + CreationDate + <Numerals from a Base64 encode of the users first and last name>
Don't put all of this in the same database, so that a Data Breach won't give an attacker all the data they need.
No Rainbows for that combination. If a user changes their user name update their hash.
This is why in good services you have to provide your password when changing your username even when you are logged in.
B. To Validate the User Name:
Step 1 is always "Check that the password meets your password Criteria. If you have a requirement that passwords are 6-24 characters, contain 1 upper, one lower, and one digit, then make sure the password has those before you do anything else. No database lookups for bad passwords. No running bcrypt on a 4 megabyte section of post data. This is how you keep Denial of service attacks from crushing your service.
C. MD5 and Sha1 are only bad because there are rainbows for them, and because you can calculate every possible combination of values for a user/pass quickly if you know a single user/pass in the system you can calculate the salt methodology if you have access to the source code as well.
I won't tell you to use either of these, but if you lose your source code, and two databases worth of data so they have user, pass, and user data, you did something else horribly wrong, or it was an inside job and nothing would have saved you. (bcrypt is expensive if you have millions of users a day logging in and not any more secure if you did the other things above.)