Rate Limiting untuk Autentikasi — Auth

Login endpoint adalah target utama: brute force, credential stuffing, dan enumeration. Rate limiting adalah lapisan pertahanan sebelum password hash bahkan dipe

Login endpoint adalah target utama: brute force, credential stuffing, dan enumeration. Rate limiting adalah lapisan pertahanan sebelum password hash bahkan diperiksa.

Yang dibatasi:

Layer Limit Tujuan
Per IP 10 req/menit Anti brute force dari satu sumber
Per username 5 req/15 menit Cegah lockout eksternal
Per IP + username 3 req/menit Kombinasi paling sempit
Global 10k req/menit Anti DoS

Algoritma umum:

1. Fixed Window (paling sederhana):

const key = `login:${ip}:${Math.floor(Date.now() / 60000)}`;
const count = await redis.incr(key);
await redis.expire(key, 60);
if (count > 10) throw new RateLimitError();

Masalah: penyerang bisa kirim 20 request di detik ke-59 dan ke-00.

2. Sliding Window (lebih akurat):

function allowRequest(key, now, windowMs, maxRequests) {
  requests[key] = (requests[key] || []).filter(
    t => now - t < windowMs
  );
  if (requests[key].length >= maxRequests) return false;
  requests[key].push(now);
  return true;
}

Simpan timestamp tiap request. Saat dicek, hanya hitung yang masuk window terakhir.

3. Token Bucket (burst-friendly):

Bucket berisi N token. Setiap request konsumsi 1 token. Token regenerate R per detik. Jika bucket kosong → tolak.

Bagus untuk API publik — user boleh burst sebentar, asal rata-rata masih di bawah rate.

Response yang tepat:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000060

Implementasi cerdas untuk login:

async function handleLogin(req, res) {
  if (!globalLimiter.allow()) return res.status(429).end();
  if (!ipLimiter.allow(req.ip)) return res.status(429).end();

  const { email, password } = req.body;
  if (!userLimiter.allow(email)) return res.status(429).end();

  const user = await authenticate(email, password);
  if (!user) {
    userLimiter.recordFail(email);
    return res.status(401).end();
  }

  return res.json({ token: issueToken(user) });
}

Jangan:

Distributed rate limiting:

Di multi-server, rate limiter harus shared — gunakan Redis dengan atomic operations (INCR + EXPIRE) atau Redis Cell module.