TL;DR
Adding client-side password hashing (like using a JavaScript library to hash the password in the browser before sending it to your server) is an extra layer of security on top of standard practices like HTTPS and server-side hashing. It doesn’t replace those, but makes things harder for attackers if your server gets compromised.
Why Add Client-Side Hashing?
You already have good security in place, right? HTTPS protects data in transit, and you hash passwords on the server before storing them. That’s great! But what happens if an attacker does manage to get into your database?
- Server Compromise: If your server is hacked, attackers could steal password hashes.
- Rainbow Tables & Brute Force: Even with strong hashing algorithms on the server, determined attackers can still try to crack passwords using rainbow tables or brute-force attacks.
Client-side hashing adds a hurdle for attackers by making stolen hashes less useful.
How Client-Side Hashing Works
- Hashing in the Browser: Use a JavaScript library (like bcrypt.js, scrypt.js or argon2-browser) to hash the password directly in the user’s browser before it’s sent to your server.
- Salt Generation: Generate a unique salt for each password on the client side. This is crucial!
- Send Hash & Salt: Send the hashed password and the salt to your server.
- Server Storage: Store both the hash and the salt in your database, associated with the user account.
- Authentication: When a user logs in:
- Hash the entered password using the stored salt for that user (on the server).
- Compare the new hash to the stored hash.
Step-by-Step Implementation
- Choose a Library: Select a suitable JavaScript hashing library. Consider factors like algorithm strength, browser compatibility and maintenance.
npm install bcryptjs - Generate Salt on Client Side: Use the library to generate a random salt for each user during registration or password change.
const salt = await bcrypt.genSalt(10); // 10 is the cost factor (higher = slower, more secure) - Hash Password on Client Side: Hash the password using the generated salt before sending it to the server.
const hash = await bcrypt.hash(password, salt); - Send Hash and Salt to Server: Send both the hash and the salt to your backend.
- Store Hash and Salt in Database: Store these values securely associated with the user’s account.
- Verification on Login: When a user attempts to log in:
- Retrieve the stored hash and salt from the database for that user.
- Hash the entered password using the retrieved salt (on the server).
- Compare the newly generated hash with the stored hash.
const match = await bcrypt.compare(enteredPassword, storedHash); // Returns true if passwords match
Important Considerations
- HTTPS is Essential: Client-side hashing only works effectively with HTTPS to prevent man-in-the-middle attacks.
- Don’t Trust the Client: Never trust data coming from the client side. Always re-hash and verify on the server.
- Server-Side Hashing Remains Crucial: Continue hashing passwords on the server using a strong algorithm (like bcrypt, Argon2 or scrypt). Client-side hashing is an addition to this, not a replacement.
- Library Security: Keep your JavaScript library updated to benefit from security patches and improvements.
- Performance Impact: Hashing in the browser can add some overhead. Choose a library that balances security with performance.

