Passwords must be safely stored in order to protect user information in any application. Here’s a guide on best practices for securely hashing and salting passwords before storing them in a relational database.
Use a Secure Hashing Algorithm
1. Avoid using general-purpose hashing algorithms like MD5, SHA-1, or even SHA-256 for password storage. These are designed for speed, making them vulnerable to brute-force and rainbow table attacks.
2. Use specialized password-hashing algorithms that are intentionally slow, such as:
- bcrypt: Widely used, battle-tested, and secure. It incorporates both salting and key stretching (slowing down the hashing process).
- scrypt: Built with memory-intensive operations, making it costly to attack with specialized hardware.
- Argon2: The latest and most secure password-hashing algorithm. Argon2 can be configured to use both time and memory constraints, making it particularly resistant to GPU-based attacks.
Add a Unique Salt for Each Password
1. Salting is the practice of adding a unique, random value (the “salt”) to each password before hashing. This ensures that even if two users have the same password, their hashes will be different.
2. Most libraries automatically generate and store a salt with each hashed password. However, if the algorithm you use doesn’t handle this, generate a strong, random salt and store it alongside the password hash in the database.
Here’s a quick example using bcrypt in Python with the bcrypt library:
import bcrypt
# To hash a password
password = "user_password".encode() # Convert to bytes
salt = bcrypt.gensalt() # Generates a random salt
hashed_password = bcrypt.hashpw(password, salt)
# Store 'hashed_password' in your database
When verifying passwords:
# User's entered password
password = "user_password".encode()
# Hash stored in the database
stored_hash = hashed_password_from_db # Retrieved from database
# Verify
if bcrypt.checkpw(password, stored_hash):
print("Password matches")
else:
print("Password does not match")
Protect Against Timing Attacks
Use a constant-time comparison function for comparing passwords. This prevents attackers from deducing information about the password by measuring response times.
Limit Password Attempts
Implement account lockouts or use techniques like exponential backoff to limit the number of password attempts within a timeframe, adding another layer of defense against brute-force attacks.
Regularly Update Hashing Standards
As hashing standards improve, update your hashing approach if necessary. You don’t need to rehash existing passwords immediately, but you can rehash passwords upon a successful login.
Store Hash and Salt in Secure Locations
Store the password hash and associated salt in the database, but ensure your database has strong access controls and is encrypted to protect against unauthorized access.