Password Storage Best Practices in Node.js
Storing passwords correctly is the difference between a breach and a non-event. Here are best practices.
Password Storage Best Practices in Node.js
Storing passwords correctly is the difference between a breach and a non-event. Here are best practices.
1. Always Hash, Never Store Plain Text
Hash with bcrypt or argon2 before saving. No exceptions. Even for test data.
2. Use a Slow Hash
bcrypt (10-12 rounds) or argon2id. Do not use SHA-256 or MD5. They are fast hashes, not password hashes.
3. Use a Salt
bcrypt and argon2 handle salting automatically. Do not roll your own. A salt ensures two users with the same password get different hashes.
4. Mark the Field With select: false
passwordHash: { type: String, required: true, select: false }
Excluded from find queries by default. Use .select('+passwordHash') only when needed (e.g., for login).
5. Use a Serializer
const toUserDTO = (user) => ({
id: user.id,
firstName: user.firstName,
email: user.email
});
res.json(toUserDTO(user));
Never include passwordHash in responses, even if select: false is set. Defense in depth.
6. Never Log Passwords or Hashes
Filter sensitive fields from logs. Use a logger that redacts known fields. A debug log accidentally containing a password is a leak.
7. Hash in a pre('save') Hook
userSchema.pre('save', async function(next) {
if (!this.isModified('passwordHash')) return next();
this.passwordHash = await bcrypt.hash(this.passwordHash, 10);
next();
});
Check isModified so profile updates do not rehash.
8. Handle Updates
For findByIdAndUpdate, hash in a pre('findOneAndUpdate') hook or in the controller. Updates bypass pre('save') by default.
9. Validate Password Strength
Require minimum 8 characters. Encourage passphrases. Reject common passwords (haveibeenpwned API or a wordlist). Do not enforce arbitrary complexity rules (uppercase, special chars) that hurt usability more than they help.
10. Rate Limit Login
5 attempts per 15 minutes per IP and per email. Prevents brute force. Lock the account temporarily after too many failures.
11. Use HTTPS
Never send passwords over HTTP. HTTPS is non-negotiable. Use Let's Encrypt for free certs.
12. Invalidate Sessions on Password Change
After a password change, invalidate all existing JWTs. Bump a tokenVersion field on the user; the auth middleware checks it.
The Takeaway
Password storage best practices: always hash, use a slow hash (bcrypt or argon2), use a salt (automatic in bcrypt/argon2), mark the field with select: false, use a serializer, never log passwords, hash in a pre('save') hook, handle updates, validate strength, rate limit login, use HTTPS, and invalidate sessions on password change.
Always hash (never plain text), use a slow hash (bcrypt or argon2), use a salt (automatic), mark the field with select: false, use a serializer, never log passwords, hash in a pre('save') hook, handle updates, validate strength, rate limit login, use HTTPS, and invalidate sessions on password change.
To exclude it from find queries by default. Without this, User.find() returns passwordHash to the client. Use .select('+passwordHash') only when needed (e.g., for login). Combined with a serializer, this is defense in depth.
Require minimum 8 characters. Encourage passphrases. Reject common passwords (haveibeenpwned API or a wordlist). Do not enforce arbitrary complexity rules (uppercase, special chars) that hurt usability more than they help.
To prevent brute-force password attacks. 5 attempts per 15 minutes per IP and per email. After too many failures, lock the account temporarily. Rate limiting is a small addition with big protection.
Bump a tokenVersion field on the user when the password changes. The auth middleware checks that the JWT's tokenVersion matches the user's current tokenVersion. After a change, old JWTs are rejected and the user must log in again.
Ready to master Node.js completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master Node.js
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

