Password Salting là kỹ thuật thêm giá trị ngẫu nhiên duy nhất (salt) vào mật khẩu trước khi băm: Hash = H(salt || password). Mục đích: chống precomputation attack (rainbow table, lookup table) và đảm bảo cùng mật khẩu → hash khác nhau.
Tại sao cần Salt?
- Không salt: Cùng password → cùng hash. Kẻ có DB hash → tra rainbow table / precomputed hash dictionary → crack hàng loạt.
- Có salt duy nhất/user: Cùng password → hash khác nhau. Kẻ phải tấn công từng user riêng lẻ (tăng chi phí tuyến tính với số user).
- Chống credential stuffing: Hash bị rò rỉ từ site A không dùng được cho site B (salt khác).
Yêu cầu kỹ thuật cho Salt
- Duy nhất (Unique): Mỗi password có salt riêng. Không reuse salt.
- Ngẫu nhiên mật mã (Cryptographically Random): Sinh từ CSPRNG (OS: /dev/urandom, getrandom(), BCryptGenRandom, libsodium randombytes). Độ dài ≥ 128-bit (16 byte), khuyến nghị 256-bit (32 byte).
- Lưu trữ công khai: Salt không cần bí mật — lưu cùng hash trong DB:
$algorithm$cost$salt$hash(modular crypt format). - Không phải secret: Salt ≠ Pepper. Pepper = secret key thêm vào (HMAC/encryption) — lưu riêng (HSM/env var), không trong DB.
Triển khai chuẩn (Modular Crypt Format - MCF)
$argon2id$v=19$m=65536,t=3,p=4$c2FsdHR2YWx1ZQ$hash_base64
$bcrypt$12$saltvalue$hashvalue
$scrypt$N=16384,r=8,p=1$salt$hash
$pbkdf2_sha256$260000$salt$hash
- Format:
$id$params$salt$hash— tự chứa thuật toán, tham số, salt, hash. - Dễ upgrade:.verify() đọc params từ hash stored, rehash nếu params cũ (upgrade on login).
Salt vs Pepper
| Thuộc tính | Salt | Pepper |
|---|---|---|
| Bí mật | Không (public, lưu DB) | Có (secret, lưu HSM/env/KMS) |
| Duy nhất/user | Có | Thường chung toàn hệ thống (hoặc rotate) |
| Mục đích | Chống precomputation, unique hash | Chống offline attack nếu DB leak (cần cả pepper) |
| Rotate | Không cần (per-user unique) | Có thể rotate (re-encrypt hash) |
Best Practice: Dùng Salt (bắt buộc) + Pepper (defense-in-depth, nếu HSM/KMS khả dụng).
Thuật toán Password Hashing khuyên dùng (2024)
- Argon2id (PHC Winner 2015): Memory-hard (m KB), Time-hard (t passes), Parallelism (p lanes). Mặc định:
m=64MB, t=3, p=4. Chống GPU/ASIC/FPGA tốt nhất. - bcrypt: Blowfish-based, cost factor (log₂ rounds). Giới hạn 72 byte input. Phổ biến, hỗ trợ rộng. Cost ≥ 12 (2024).
- scrypt: N, r, p params. Memory-hard gốc. Dùng Litecoin. Ít support hơn Argon2.
- PBKDF2-HMAC-SHA256: Chỉ time-hard (iteration count). Chỉ dùng khi bắt buộc FIPS/legacy. Iterations ≥ 600,000 (2024).
Sai lầm thường gặp
- Dùng salt cố định / hardcode / timestamp / username làm salt.
- Dùng hash nhanh (SHA-256, MD5) + salt → vẫn bị GPU brute-force nhanh.
- Không lưu salt / lưu riêng hash → không verify được.
- Salt quá ngắn (< 64-bit) → collision salt khả thi.
- Không upgrade hash params khi user login (rehash với params mới).
Code mẫu (Python - passlib / argon2-cffi)
from passlib.hash import argon2
# Hash (register / change password)
hash = argon2.hash(password) # auto gen salt, default params
# Verify (login)
if argon2.verify(password, stored_hash):
# Optional: upgrade if params outdated
if argon2.needs_update(stored_hash):
new_hash = argon2.hash(password)
db.update(user_id, new_hash)
Tóm lại: Salt = giá trị ngẫu nhiên duy nhất per-password, lưu công khai, bắt buộc cho mọi password hashing. Kết hợp với KDF chậm (Argon2id/bcrypt) + optional Pepper = bảo vệ mật khẩu tiêu chuẩn vàng 2024.