Description
A fast hash such as MD5 lets an attacker test many candidate passwords quickly after a database breach. This is password guessing by comparing hashes, not decryption of the hash. Without a unique salt, attackers can also use precomputed hash lists and compare hashes across users.
Potential impact
- Rapid offline guessing may reveal users' passwords.
- Recovered passwords may let an attacker access accounts without permission.
Remediation
- Do not use fast hashes such as MD5 or SHA-1 for password storage.
- Use a maintained Argon2id or scrypt implementation. If retaining bcrypt for an existing system, account for its input limit and choose a suitable computation cost.
- Let the library generate a unique salt and store the cost parameters with the hash. Salts make precomputation and comparisons of identical passwords harder; they do not prevent hash collisions.
Examples
Before
javascript
const crypto = require('crypto');
function setUserPassword(user, pwtext) {
const hash = crypto.createHash('md5').update(pwtext).digest('hex');
user.setPassword(hash);
}
After
javascript
const bcrypt = require('bcrypt');
function setUserPassword(user, pwtext) {
const saltRounds = 12; // Example cost: tune for the service environment
bcrypt.hash(pwtext, saltRounds, function(err, hash) {
if (err) throw err;
user.setPassword(hash);
});
}
The first excerpt uses fast MD5 without a unique salt, making candidate testing inexpensive. The second lets bcrypt generate the salt and apply a cost factor, increasing the cost of guessing. The sample cost of 12 is not a universal guarantee: tune it for the service, handle bcrypt's 72-byte input limit, and send hashing failures through the application's error-handling path.