Penyamaran Respon Masuk & Kunci Akun Sementara
Mencegah pihak luar menebak apakah suatu email terdaftar atau tidak, serta mengunci sementara jika ada yang salah memasukkan kata sandi berkali-kali.
Mencegah pihak luar menebak apakah suatu email terdaftar atau tidak, serta mengunci sementara jika ada yang salah memasukkan kata sandi berkali-kali.
Tantangan & Kebutuhan Pengguna
Celah klasik pada formulir login adalah perbedaan waktu respons (*timing side-channel*): ketika email tidak ditemukan di database, server langsung membalas dalam hitungan <5 milidetik karena melewati proses hashing `bcrypt.compare`, sedangkan jika email terdaftar, server membutuhkan ~250 milidetik. Penyerang dapat memanfaatkan selisih waktu ini untuk memanen daftar email aktif. Selain itu, jika penguncian (*lockout*) hanya dihitung berdasarkan alamat email saja, penyerang bisa sengaja mengunci akun admin dari luar (*denial of service*).
Baik email terdaftar maupun tidak terdaftar, server harus bekerja dengan beban komputasi dan catatan jejak yang identik agar tidak ada satu bit pun informasi keberadaan akun yang bocor.
Solusi & Cara Kerja
Layanan `authenticate()` di `modules/public/auth/auth.service.ts` menyiapkan sebuah `dummyHash` bcrypt dengan cost factor `12` yang sama persis dengan password asli. Ketika email yang dimasukkan tidak ada di tabel `users`, `bcrypt.compare` tetap dijalankan terhadap `dummyHash`, dan kegagalannya tetap dicatat ke tabel `login_attempts`. Pembatasan percobaan gagal dijalankan pada dua sumbu sekaligus menggunakan `Promise.all`: batas ketat untuk kombinasi spesifik `(email, ip)` dan batas lebih longgar untuk keseluruhan `ip` agar satu pengguna di jaringan kantor yang salah ketik tidak langsung memblokir rekan sekantornya.
Berikut adalah implementasi asli pemeriksaan lockout dua sumbu dan verifikasi waktu konstan di dalam `modules/public/auth/auth.service.ts`:
Detail & Hal yang Diperhatikan
- Penanganan IP Tidak Dikenal (`unknown`): Jika aplikasi berjalan tanpa header IP dan `ip === 'unknown'`, sistem menerapkan plafon khusus `LOGIN_MAX_ATTEMPTS_UNKNOWN_IP` agar seluruh pengguna di balik koneksi anonim tidak langsung terkunci oleh lima kesalahan ketik saja.
- Reset Kuota Saat Berhasil & Retensi Otomatis: Begitu pengguna berhasil masuk, catatan kegagalan untuk pasangan `(email, ip)` tersebut langsung dibersihkan, sementara baris lama yang melampaui masa retensi (`LOGIN_RETENTION_MS`) otomatis dihapus.
- Pemantauan Langsung oleh Admin: Seluruh rekaman kegagalan di tabel `login_attempts` dapat dipantau secara real-time oleh administrator melalui halaman `/dashboard/login-attempts`.