Kunci Keamanan Tambahan via Aplikasi Autentikator
Melindungi akun dari pembobolan kata sandi dengan kode angka 6 digit dari aplikasi seperti Google Authenticator yang selalu berganti setiap 30 detik.
Melindungi akun dari pembobolan kata sandi dengan kode angka 6 digit dari aplikasi seperti Google Authenticator yang selalu berganti setiap 30 detik.
Tantangan & Kebutuhan Pengguna
Akun dengan hak akses tinggi seperti Super Admin merupakan target utama serangan pengambilalihan akun. Namun banyak implementasi MFA menyimpan secret TOTP secara plaintext di dalam database dan menerbitkan sesi login sebelum kode OTP diverifikasi. Jika tabel pengguna bocor melalui backup atau celah SQL, penyerang dapat langsung mencetak kode 6 digit sendiri.
Rahasia MFA di dalam database tidak boleh disimpan telanjang; bahkan jika isi tabel bocor, penyerang tetap tidak boleh mampu membangkitkan kode OTP tanpa kunci enkripsi server.
Solusi & Cara Kerja
Proses autentikasi untuk akun ber-MFA dipisah menjadi dua tahap tiket yang terisolasi. Saat email dan password cocok, server belum membuat sesi di `user_sessions`, melainkan hanya mengeluarkan cookie `mfa_challenge` (`SameSite=Strict`, TTL 5 menit, klaim `audience: 'mfa-challenge'`). Secret TOTP 160-bit dienkripsi menggunakan algoritma **AES-256-GCM** dengan IV 12-byte acak dan authentication tag yang disimpan dalam format berversi `v1.<iv>.<tag>.<ciphertext>`. Selain itu, 10 kode pemulihan darurat (*recovery codes*) di-hash satu arah menggunakan SHA-256 ber-prefix domain dan diverifikasi menggunakan `timingSafeEqual`.
Berikut adalah implementasi asli enkripsi AES-256-GCM dan verifikasi TOTP tahan *timing attack* di dalam `common/utils/totp.ts`:
Detail & Hal yang Diperhatikan
- Toleransi Pergeseran Jam (Clock Skew): Fungsi `verifyTotp` memeriksa jendela waktu `window = 1` (±30 detik dari step 30 detik saat ini) agar sedikit perbedaan jam antara smartphone pengguna dan server tidak menyebabkan penolakan palsu.
- Penghapusan Atomik Recovery Code: Saat pengguna masuk memakai salah satu recovery code, hash kode tersebut langsung dibuang dari array `mfaRecoveryCodes` di dalam `prisma.$transaction` bersama pembersihan `mfaAttempt` sehingga tidak bisa dipakai ulang.
- Kebijakan Wajib MFA Super Admin: Jika `ENFORCE_SUPER_ADMIN_MFA=true` diaktifkan, fungsi `requirePageAccess` otomatis mengalihkan Super Admin yang belum mengaktifkan MFA ke `/dashboard/security?setup_required=1` dan `requirePermission` memblokir semua mutasi.
Gunakan variabel lingkungan MFA_ENCRYPTION_KEY yang terpisah dari SESSION_SECRET pada lingkungan produksi agar rotasi kunci sesi tidak memengaruhi keterbacaan secret MFA yang tersimpan di database.