TL;DR
Storing usernames (emails) and passwords together in a single hashed field is insecure. This guide explains how to separate them, hash each individually with strong algorithms, and store the hashes securely for better cyber security.
Solution Guide: Separating & Hashing User Credentials
- Understand the Risk
- Combining usernames and passwords before hashing creates a ‘rainbow table’ vulnerability. If one user has a weak password, attackers can potentially compromise other accounts using similar patterns.
- It makes salt application more difficult and less effective.
Modify your database to store usernames (emails) and password hashes in separate columns.
- Replace the existing single ‘credentials’ field with two fields:
username: VARCHAR(255), UNIQUE index recommended.password_hash: VARCHAR(255) or larger, depending on your hashing algorithm (see step 3).
Use modern, robust password hashing algorithms. Avoid older algorithms like MD5 and SHA1.
- Recommended options:
- bcrypt: A well-established algorithm with automatic salting.
- Argon2: Considered the most secure option, but may require more computational resources.
- scrypt: Another strong option, offering good security and performance.
When a new user registers, hash their password before storing it in the database.
# Example using Python and bcrypt
import bcrypt
def hash_password(password):
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
return hashed_password.decode('utf-8')
# Example usage:
new_user_password = "MySecurePassword123"
hashed_password = hash_password(new_user_password)
print(hashed_password) # Store this in the password_hash column
When a user logs in, retrieve their stored password_hash and compare it to the hash of the entered password.
# Example using Python and bcrypt
import bcrypt
def verify_password(entered_password, stored_hash):
return bcrypt.checkpw(entered_password.encode('utf-8'), stored_hash.encode('utf-8'))
# Example usage:
stored_hash = "$2b$12$EXAMPLEHASHEDPASSWORD"
user_entered_password = "MySecurePassword123"
is_valid = verify_password(user_entered_password, stored_hash)
print(is_valid) # True if the password matches
If your chosen algorithm doesn’t handle salting automatically, you must add a unique salt to each password before hashing.
- Generate a random salt for each user. Store the salt alongside the hash (e.g., in another database column).
- Concatenate the salt with the password before hashing:
hash(salt + password)
- Key Stretching: Algorithms like bcrypt and Argon2 perform key stretching, making brute-force attacks more difficult.
- Regular Updates: Keep your hashing libraries up to date to benefit from the latest security improvements.
- Rate Limiting: Implement rate limiting on login attempts to prevent brute-force attacks.
- Two-Factor Authentication (2FA): Consider adding 2FA for an extra layer of cyber security.