Phối nhiều lớp, không dựa vào một biện pháp duy nhất:
- Rate limit theo IP và theo tài khoản (vd tối đa N lần/phút). Khoá theo account chống việc đổi IP; khoá theo IP chống spray một IP nhiều account.
- Exponential backoff: mỗi lần sai tăng dần thời gian chờ (1s, 2s, 4s...) thay vì khoá cứng — làm chậm bot mà ít phiền user thật.
- Account lockout tạm thời sau nhiều lần sai (kèm cẩn trọng: khoá cứng dễ bị lợi dụng làm DoS khoá tài khoản người khác → ưu tiên backoff + cảnh báo).
- CAPTCHA bật lên sau vài lần thất bại (không bật ngay từ đầu để đỡ phiền).
- Chống credential stuffing: MFA, kiểm tra mật khẩu lộ (haveibeenpwned), theo dõi đăng nhập bất thường (thiết bị/địa điểm lạ).
- Thông báo lỗi mơ hồ ("sai email hoặc mật khẩu") để không tiết lộ email nào tồn tại.
js
// đếm số lần sai theo account+IP trong cửa sổ cố định tính từ lần sai đầu (Redis; sliding window thật thì cần ZSET timestamp)
const key = `login:fail:${email}:${ip}`
const fails = await redis.incr(key)
if (fails === 1) await redis.expire(key, 900) // cửa sổ 15 phút
if (fails > 5) throw new Error("too many attempts, slow down")
// khi đăng nhập thành công: await redis.del(key)Lỗi hay gặp: chỉ rate-limit theo IP (bot dùng proxy pool xoay IP dễ vượt), hoặc lockout cứng thành lỗ hổng DoS.