Is there more to password hashing?
Multiple Iterations - This helps. Its called key-strengthening and it raises the computational complexity. E.g., if you key-strengthen by a factor of a 1000, an attacker can try 1000 less possible passwords in the same amount of GPU/CPU time.
Multiple hash functions - In theory this helps but not really in practice. The case would be if one hash function used is somehow broken (e.g., you can reverse any 256-bit hash by trying significantly less than 2256 passwords or can significantly simplify the brute-forcing process while trying common passwords), but the other hash functions used in the chain are not broken. E.g., that your attacker has broken iterated sha512, so can quickly come up with a password that works for sha512 key-strengthened N times, but not
sha512(salt||bcrypt(salt||sha512(salt||pw)))
. However in my view with modern cryptographic hash functions and typical attackers, that's a fairly unlikely attack scenario -- there are much lower hanging fruit that don't require the attacker to be make fundamental advances in computer science. Possibly NSA or serious academic computer scientist may come up with clever ways to break hashing algorithms, but not your average malicious attacker. Also even among hash algorithms that are broken and should generally be avoided (e.g., MD5 and SHA-1), they are usually only broken to collision attacks (find a pair of distinct stringss1
,s2
that satisfieshash(s1) == hash(s2)
much quicker than expected), but not preimage attacks (given a hashh
find ans
that satisfieshash(s)=h
), which is the relevant attack on a password hash.Salt mixing - This is an implementation detail, and doing it adds no complexity to an attacker. (Precomputed rainbow tables for complex key-strengthened salted hashes don't exist.)
Unique schemes - Again an implementation detail, and adds no security. Also don't run bcrypt multiple times; that's silly. Bcrypt is already key-strengthened. E.g., if you hash using bcrypt with a cost of 16, that means the key went through 2^16 ~ 65536 rounds, so if you want a stronger hash just increase the cost (every increase by 1; means bcrypt will be twice as slow).
Black Magic - As a programmer, I wouldn't suggest doing this; as black magic is a maintainance nightmare for you and your team, and again not too significant for an attacker to overcome. Decision comes down to adopt a new technology, so you have to rewrite the non-standard hash routine in a new language on a new platform and unless its very well-documented you may do it slightly differently breaking the system. And if it is well-documented, an attacker may steal your source code; see what the function does to a password and then can use for their brute forcing. If they manage to steal your hashes, they probably have at least one account where they know a password and stole the hash, so can try reverse engineering it (even if they didn't get the source code). Its like obsfucating your source code on your own server. Its more a hassle for you the developer/maintainer, not really for attackers.
Obsfuscation - Don't do this. See black magic. Or just operate on Kerckhoff's principle aka Shannon's maxim: make the system strong even if the enemy perfectly knows the system. Obfuscation/black magic can be overcome with a little analysis and provides some security (defense-in-depth), but not much. However, a strong key-strengthened cryptographic hash with a high-entropy initial passphrase/password can easily be built to have a computational complexity that is completely unfeasible to attack with all the computers in the universe for thousands of years, without major advances in computer science (e.g., quantum computer or breaking the hash function).
In the end whatever is done with the password it will not prevent someone using a stupid password (like 'password' or '123')
Actually I'll disagree on this one. Depending on how captive the audience is (e.g., workers must use it for their job), you could invalidate simple passwords whenever a new password is setup (e.g., check against a leaked password list of a ~million common passwords and invalidate all matches, or doesn't pass other minimum standards: must be 8+ characters with upper/lower/number/symbol or a 20+ character passphrase). Basically, if its a secure application that has serious repercussions for your end if an account is breached, something like this is a great idea.
Granted users will have negative user experience if you are too strict (so if you are a simple web app or an online retailer that wants lots of users and doesn't really worry about accounts being stolen from weak passwords, you probably do not want to implement strict password rules; but the retailer should do things like require re-entering credit card information after the email/shipping address changes and should send an receipt email after all purchases). Also, beware this ultimately weakening security by users posting the password on their monitor in a common work environment; so make it clear that there are consequences to a user's account being breached.