TL;DR
This guide shows you how to check if a user’s entered password matches a stored salted hash without needing the username. It focuses on secure comparison techniques to prevent timing attacks and other vulnerabilities.
Steps
- Understand the Setup
- You have a database storing user passwords as salted hashes. This means each password was combined with a unique ‘salt’ before being hashed (e.g., using bcrypt, Argon2).
- You need to verify if a provided password matches the stored hash for a specific user. Crucially, you *don’t* have the username available at this stage – only the hash and salt.
- Retrieve Salt and Hash
- Hash the Entered Password
- Securely Compare the Hashes
- Use a constant-time comparison function provided by your hashing library. These functions are designed to take the same amount of time regardless of whether the hashes match or not.
- Argon2 Example (Alternative)
- Handle Errors and Edge Cases
- Check for null or empty salt/hash values in your database.
- Ensure you are using a strong hashing algorithm (bcrypt, Argon2) with appropriate parameters (work factor/memory cost).
- Implement proper error handling to prevent information leaks.
Fetch the salt and corresponding hash from your database. The method depends on how you store them (e.g., separate columns, JSON object). For example, using SQL:
SELECT password_hash, salt FROM users WHERE user_id = 123;
Use the same hashing algorithm (e.g., bcrypt) and the retrieved salt to hash the password entered by the user.
import bcrypt
salt = "your_retrieved_salt"
hashed_password = bcrypt.hashpw(entered_password.encode('utf-8'), salt.encode('utf-8'))
This is the most important step! Never directly compare strings using ==. Timing attacks can reveal information about the hash.
import bcrypt
hashed_password = "your_hashed_password"
entered_password_hash = "your_newly_hashed_password"
if bcrypt.checkpw(entered_password_hash.encode('utf-8'), hashed_password.encode('utf-8')):
print("Password matches!")
else:
print("Password does not match.")
Important: The bcrypt.checkpw() function handles the secure comparison for you.
If using Argon2, the process is similar:
import argon2
ph = argon2.PasswordHasher()
try:
argon2.verify(hashed_password, entered_password)
print("Password matches!")
except argon2.exceptions.VerifyMismatchError:
print("Password does not match.")
Important: Argon2’s verify() function performs the secure comparison.

