DDevArchive
Đăng nhập

Password hashing & bcrypt: không bao giờ lưu mật khẩu plaintext

Lưu mật khẩu dạng chữ là lỗi bảo mật nghiêm trọng nhất — một vụ breach lộ hết tài khoản. Bcrypt biến mật khẩu thành chuỗi hash không thể đảo ngược, và salt đảm bảo hai người cùng mật khẩu vẫn có hash khác nhau.

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ưuKhi bị breachMức nguy hiểm
Plaintext: 123456Hacker đọ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 = 10 là đủ

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