In the process of building what I'd like to hope is a properly-architected authentication mechanism, I've come across a lot of materials that specify that:
- user passwords must be salted
- the salt used should be sufficiently random and generated per-user
- ...therefore, the salt must be stored with the user record in order to support verification of the user password
I wholeheartedly agree with the first and second points, but it seems like there's an easy workaround for the latter. Instead of doing the equivalent of (pseudocode here):
salt = random();
hashedPassword = hash(salt . password);
storeUserRecord(username, hashedPassword, salt);
Why not use the hash of the username as the salt? This yields a domain of salts that is well-distributed, (roughly) random, and each individual salt is as complex as your salt function provides for. Even better, you don't have to store the salt in the database -- just regenerate it at authentication-time. More pseudocode:
salt = hash(username);
hashedPassword = hash(salt . password);
storeUserRecord(username, hashedPassword);
(Of course, hash
in the examples above should be something reasonable, like SHA-512, or some other strong hash.)
This seems reasonable to me given what (little) I know of crypto, but the fact that it's a simplification over widely-recommended practice makes me wonder whether there's some obvious reason I've gone astray that I'm not aware of.
EDIT Some appear to not grok what the question is. I no way am I suggesting that no salt be used. Referring to TheRook's edited answer: I'm familiar with the references noted in those CWE's. The core question I have is: why is hash(username) a predictable salt?
EDIT 2 Thanks to all those that provided answers; biffabacon directly addressed my core question in his 2nd paragraph (basically, anything you can do to maximize the domain of the salts being used and therefore the hashed passwords being generated is good), but there's lots of tasty info in various comments on this question.