Using appropriate hashing algorithms, appropriately putting extra security measures in place, and following best practices are all necessary to prevent dictionary attacks on password hashes.
1. Use a Strong Password Hashing Algorithm
- bcrypt: An adaptive hash function designed specifically for password storage. It incorporates a salt and is computationally expensive, making it resistant to dictionary attacks. Its adaptive nature means it slows down as computing power increases, maintaining security over time.
- Argon2: The winner of the Password Hashing Competition (2015), Argon2 provides excellent protection against both GPU-based dictionary attacks and side-channel attacks. It offers three versions (Argon2d, Argon2i, Argon2id), with Argon2id being the most recommended for its balanced protection against both types of attacks.
- PBKDF2 (with a sufficient iteration count): While not as modern as bcrypt or Argon2, PBKDF2 can still be secure if used with a high enough iteration count (e.g., 100,000 or more) and a sufficiently long salt. However, bcrypt and Argon2 are generally preferred for new applications.
2. Properly Implement Salting
- Use a Unique Salt per User: Ensure each user's password is hashed with a unique salt. This prevents attackers from using precomputed tables (rainbow tables) even if multiple users have the same password.
- Generate Salts Securely: Use a cryptographically secure pseudo-random number generator (CSPRNG) to generate salts. The length should be sufficient; typically, 16 bytes (128 bits) or more is recommended.
3. Hashing Iterations/Work Factor
For algorithms that support it (like bcrypt, Argon2, and PBKDF2), increase the work factor or iteration count as high as possible without significantly impacting your application's performance. This directly increases the computational cost for attackers.
4. Additional Best Practices
- Regularly Update Your Hashing Strategy: As computing power increases and vulnerabilities are discovered, it's crucial to periodically re-hash existing passwords with updated, stronger parameters (e.g., increasing bcrypt's cost factor or switching from PBKDF2 to Argon2).
- Enforce Strong Password Policies: While not directly related to hashing, ensuring users have strong, unique passwords reduces the effectiveness of dictionary attacks. Implement policies for password length, complexity, and rotation.
- Monitor for Breaches: Regularly check if your users' email addresses or passwords have appeared in known breaches using services like Have I Been Pwned. Encourage (or enforce) password changes for affected accounts.
- Educate Users: Inform your users about the importance of password security, the risks of password reuse, and how to generate strong passwords.
Example Implementations
1. Using Bcrypt in Python
import bcrypt
def hash_password(password):
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
return hashed_password
def check_password(stored_password, provided_password):
return bcrypt.checkpw(provided_password.encode('utf-8'), stored_password)
2. Using Argon2 in Python
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
ph = PasswordHasher()
def hash_password(password):
return ph.hash(password)
def check_password(hashed_password, provided_password):
try:
return ph.verify(hashed_password, provided_password)
except VerifyMismatchError:
return False