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). Khóa theo account chống việc đổi IP; khóa 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ì khóa 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: khóa cứng dễ bị lợi dụng làm DoS khóa 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
// count failed attempts per account+IP in a rolling window (Redis)
const key = `login:fail:${email}:${ip}`
const fails = await redis.incr(key)
if (fails === 1) await redis.expire(key, 900) // 15-min window
if (fails > 5) throw new Error("too many attempts, slow down")
// on successful login: await redis.del(key)Bẫy: 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.