Why MD5 and SHA-1 Are No Longer Secure for Password Hashing
Understanding the security implications of using deprecated hash algorithms and what to use instead.
Quick answer
MD5 and SHA-1 are broken or deprecated for password storage because collisions and GPU cracking are practical. Use Argon2id or bcrypt with per-user salts. The Hash Generator can compare legacy digests for migration debugging — not for storing new passwords.
Key takeaways
- ›MD5 collisions can be generated — never use MD5 for passwords.
- ›SHA-1 collision attacks are practical — treat SHA-1 like MD5 for security use cases.
- ›Fast hashes (MD5, SHA-256) without salts are vulnerable to brute force and rainbow tables.
- ›Use adaptive password hashing: Argon2id or bcrypt with an appropriate cost factor.
Apply this guide with the Hash Generator
Open Hash GeneratorHash algorithms are fundamental to web security, but not all hashes are created equal. MD5 and SHA-1, once industry standards, are now considered cryptographically broken and should never be used for password hashing or any security-critical applications. The Hash Generator can help you compare legacy digests during migration — not for storing new passwords.
The Problem with MD5
MD5 (Message Digest Algorithm 5) was designed in 1991 and was widely used for cryptographic purposes. However, it has several critical vulnerabilities:
Collision Vulnerabilities
MD5 is vulnerable to collision attacks, where two different inputs produce the same hash:
# Example of MD5 collision (simplified)
Input 1: "Hello World"
MD5: 5d41402abc4b2a76b9719d911017c592
Input 2: "Different input"
MD5: 5d41402abc4b2a76b9719d911017c592 # Same hash!Speed and Brute Force
MD5 is extremely fast, making it vulnerable to brute force attacks:
// MD5 can hash millions of passwords per second
const crypto = require('crypto');
const start = Date.now();
for (let i = 0; i < 1000000; i++) {
crypto.createHash('md5').update('password' + i).digest('hex');
}
console.log(`Time: ${Date.now() - start}ms`); // Very fast!The Problem with SHA-1
SHA-1 (Secure Hash Algorithm 1) was designed in 1995 and was considered secure until 2005 when theoretical attacks were discovered. In 2017, Google demonstrated a practical collision attack.
Practical Collision Attack
Google's SHAttered attack showed that SHA-1 collisions can be generated in practice:
# Two different PDF files with identical SHA-1 hashes
File 1: shattered-1.pdf
SHA-1: 38762cf7f55934b34d179ae6a4c80cadccbb7f0a
File 2: shattered-2.pdf
SHA-1: 38762cf7f55934b34d179ae6a4c80cadccbb7f0a # Same hash!Why These Algorithms Are Dangerous
1. Rainbow Table Attacks
Fast hash algorithms are vulnerable to rainbow table attacks:
// Rainbow tables can precompute hashes for common passwords
const commonPasswords = ['password', '123456', 'admin', 'qwerty'];
const rainbowTable = {};
commonPasswords.forEach(pwd => {
rainbowTable[crypto.createHash('md5').update(pwd).digest('hex')] = pwd;
});
// Instant password recovery
function crackPassword(hash) {
return rainbowTable[hash] || 'Not found in rainbow table';
}2. GPU Acceleration
Modern GPUs can perform billions of hash operations per second:
# Using hashcat for GPU-accelerated cracking
# hashcat -m 0 -a 3 hashes.txt ?a?a?a?a?a?a?a?a
# This can crack 8-character passwords in minutesWhat to Use Instead
1. bcrypt (Recommended)
bcrypt is specifically designed for password hashing and includes a cost factor:
const bcrypt = require('bcrypt');
// Hash a password
const saltRounds = 12;
const hashedPassword = await bcrypt.hash('myPassword', saltRounds);
// Verify a password
const isValid = await bcrypt.compare('myPassword', hashedPassword);2. Argon2 (Winner of Password Hashing Competition)
Argon2 is the recommended algorithm by security experts:
const argon2 = require('argon2');
// Hash a password
const hashedPassword = await argon2.hash('myPassword', {
type: argon2.argon2id,
memoryCost: 2 ** 16, // 64 MB
timeCost: 3, // 3 iterations
parallelism: 1
});
// Verify a password
const isValid = await argon2.verify(hashedPassword, 'myPassword');3. scrypt
scrypt is designed to be memory-hard and resistant to hardware attacks:
const scrypt = require('scrypt');
const hashedPassword = scrypt.hashSync('myPassword', {
N: 16384, // CPU/memory cost
r: 8, // block size
p: 1 // parallelization
});Migration Strategy
If you're currently using MD5 or SHA-1, here's how to migrate:
1. Immediate Steps
- Stop using MD5/SHA-1 for new passwords
- Implement proper password hashing (bcrypt/Argon2)
- Add password strength requirements
2. Gradual Migration
async function verifyPassword(password, hash) {
// Check if it's an old MD5/SHA-1 hash
if (hash.length === 32) { // MD5
const oldHash = crypto.createHash('md5').update(password).digest('hex');
if (oldHash === hash) {
// Rehash with bcrypt and update database
const newHash = await bcrypt.hash(password, 12);
await updateUserPassword(userId, newHash);
return true;
}
}
// Verify with new bcrypt hash
return await bcrypt.compare(password, hash);
}3. Force Password Reset
For high-security applications, consider forcing users to reset passwords:
// Mark old hashes for forced reset
const needsReset = hash.length === 32 || hash.length === 40; // MD5 or SHA-1
if (needsReset) {
return { error: 'Password must be reset for security reasons' };
}Best Practices
- Use a high cost factor: bcrypt with cost 12+ (takes ~250ms)
- Add salt: All modern algorithms include salt automatically
- Use unique salts: Each password should have a unique salt
- Implement rate limiting: Prevent brute force attacks
- Monitor for breaches: Check if passwords appear in known breaches
Conclusion
MD5 and SHA-1 are cryptographically broken and should never be used for password hashing. The security of your application depends on using proper password hashing algorithms like bcrypt, Argon2, or scrypt. The small performance cost of these algorithms is insignificant compared to the security benefits they provide.
Remember: security is not about making attacks impossible, but about making them impractical. Modern password hashing algorithms achieve this by being intentionally slow and memory-intensive, making brute force attacks economically unfeasible.