Storing passwords is a difficult task when we need to satisfy all the data protection laws, and it is getting even tougher with the rise of GDPR, the new European regulation in data privacy. Therefore, we have to make sure all our sensitive data is protected, and for passwords specifically that means hashing rather than encrypting them.
Hashing is not encryption. Encryption is reversible — whoever holds the key can recover the original value — whereas a hash is a one-way function with no way back. That is exactly the property you want for passwords: you never need to read a password back, only to check whether a supplied one matches, so a leaked database of hashes does not hand the attacker the passwords themselves.
When we want to hash passwords to store them in a database, Bcrypt is the way to go and there are many libraries for different languages.
Java Implementation
If your language of choice is Java you can use BCrypt with the library included in the Spring Security module.
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
JBcrypt hashes passwords using a version of Bruce Schneier’s Blowfish block cipher with modifications designed to raise the cost of off-line password cracking. The computation cost of the algorithm is parameterized, so it can be increased as computers get faster.
Operations:
-
Hash password:
BCrypt.hashpw(plainTextPassword, BCrypt.gensalt())where
plainTextPasswordis the password we want to hash andBCrypt.gensalt()is a salt generated randomly.In case we want to increment the complexity, an optional parameter (
log_rounds) has to be provided toBCrypt.gensalt(), which determines the computational complexity of the hashing.log_roundsis exponential (\(2^{log\_rounds}\)) and it specifies how many times to run the internal hash function. The default value is 10, and the valid values are between 4 and 31.The output strings after running
hashpw()will look like:$bcrypt_id$log_rounds$128_bits_salt184_bits_hashhashpw()is smart enough to extract the salt from the string, so you do not need to worry about the salt value anymore, only the hashed string. -
Check hashed password:
BCrypt.checkpw(unencryptedPassword, hashPassword)This method hashes the supplied plaintext password using the salt it reads out of
hashPassword, then compares the two hashes and returns whether they match.
Conclusion
BCrypt is a very good hash algorithm for preventing rainbow table attacks, because it keeps the salt as part of the output from the BCrypt function. The idea is that every password gets a unique salt that is incorporated into the hash, so the same password produces a different hash for every user. A precomputed table is only useful against one salt, so an attacker would have to build a separate table per password — which is what makes the approach infeasible rather than merely slow.
A Rainbow table is a precomputed structure that maps hash values back to the plaintext that produced them, letting an attacker reverse a stolen hash by lookup instead of by guessing. It trades memory for time by storing chains of repeated hash-and-reduce steps rather than every pair outright. Hackers can use it for cracking unsalted hashed passwords stored in a database.
Bcrypt is 10,000 times slower than sha1 to run. If we have a machine that is able to run it in 100ms, this is probably fast enough for login, but it might be too slow if we want to execute Bcrypt against a long list of passwords. In consequence, if a hacker wants to run Bcrypt a billion times by using the same computational power, it will take 27,777 hours.
