Vì sao phải hash mật khẩu?
Nếu database bị hack, hacker đọc bảng users. Mật khẩu plaintext → lộ hết. Hash → hacker chỉ thấy chuỗi vô nghĩa, không thể dùng đăng nhập. Hash là hàm một chiều: từ mật khẩu tính ra hash dễ, từ hash tìm lại mật khẩu gần như bất khả thi.
| Cách lưu | Khi bị breach | Mức nguy hiểm |
|---|---|---|
Plaintext: 123456 | Hacker đọc trực tiếp | 🔴 Cực kỳ nguy hiểm |
MD5/SHA: e10adc… | Tra rainbow table → tìm được | 🟠 Vẫn nguy hiểm |
Bcrypt: $2b$10$xyz… | Phải brute force từng cái, cực chậm | 🟢 An toàn |
Bcrypt: hash + salt tự động
Salt là chuỗi ngẫu nhiên gắn vào mật khẩu trước khi hash. Hai người dùng cùng mật khẩu “123456” sẽ có hash khác nhau. Bcrypt tự tạo salt và gắn vào kết quả — bạn không cần quản lý salt riêng.
// File: hash.js
import bcrypt from 'bcryptjs'
// Đăng ký: hash mật khẩu trước khi lưu vào DB
async function register(email, password) {
const SALT_ROUNDS = 10
const passwordHash = await bcrypt.hash(password, SALT_ROUNDS)
// Lưu hash, KHÔNG BAO GIỜ lưu password gốc
await db.users.create({ email, passwordHash })
}
// Đăng nhập: so sánh password với hash đã lưu
async function verifyPassword(inputPassword, storedHash) {
const isMatch = await bcrypt.compare(inputPassword, storedHash)
return isMatch // true hoặc false
}
SALT_ROUNDS càng cao càng chậm (mỗi +1 chậm gấp đôi). 10 rounds ≈ 100ms trên server hiện đại — đủ chậm để chống brute force, đủ nhanh để không lag khi đăng nhập.
Các sai lầm hay gặp
(1) Dùng MD5/SHA1 — quá nhanh, hacker brute force hàng tỷ hash/giây. (2) Tự tạo hàm hash — gần như chắc chắn có lỗ hổng. (3) Dùng encryption thay hash — encryption giải ngược được, hash thì không. (4) Quên validate độ dài mật khẩu — cho phép mật khẩu 1 ký tự.
❓ Hai người dùng có cùng mật khẩu "abc123". Hash bcrypt của họ sẽ:
- Giải thích vì sao không được lưu mật khẩu plaintext
- Phân biệt hash (một chiều) và encryption (hai chiều)
- Dùng bcrypt.hash() và bcrypt.compare()
- Chọn salt rounds phù hợp