Home / Artikel / Web Security
Web Security

Password Reset Bukan Sekadar Kirim Link: Cara Mendesain Pemulihan Akun yang Aman

Fitur “Lupa Password” sering dianggap bagian kecil dari sistem login, padahal alur inilah yang bisa membuka pintu ketika perlindungan utama gagal. Kenali cara membuat password reset yang tidak membocorkan akun, mudah di…

Password Reset Bukan Sekadar Kirim Link: Cara Mendesain Pemulihan Akun yang Aman

Fitur “Lupa Password” sering baru diperhatikan setelah ada pengguna yang tidak bisa masuk. Padahal, dari sisi keamanan, fitur ini adalah jalur alternatif menuju akun. Jika jalurnya lebih lemah daripada login biasa, penyerang tidak perlu menebak password—mereka cukup mencari cara menyalahgunakan proses pemulihan.

Masalahnya bisa muncul dalam bentuk yang sederhana: website memberi tahu bahwa alamat email tidak terdaftar, token reset bisa dipakai berkali-kali, atau semua sesi lama tetap aktif setelah password diganti. Untuk website kecil, toko online, maupun aplikasi PHP, beberapa keputusan desain yang tepat dapat mengurangi risiko ini secara signifikan.

Mulai dengan pesan yang tidak membocorkan akun

Kesalahan umum terjadi di halaman permintaan reset password. Pengguna memasukkan alamat email, lalu website menjawab secara berbeda berdasarkan apakah email tersebut ada di database.

  • “Email reset sudah dikirim” berarti akun tersebut ada.
  • “Email tidak ditemukan” berarti akun tersebut tidak ada.

Perbedaan ini memungkinkan user enumeration, yaitu teknik untuk mengumpulkan daftar akun yang terdaftar. Daftar tersebut bisa dipakai untuk serangan phishing, credential stuffing, atau membanjiri inbox korban dengan permintaan reset.

Gunakan pesan yang sama untuk kedua kondisi, misalnya: “Jika alamat tersebut terdaftar, kami akan mengirimkan instruksi pemulihan.” Jangan hanya menyamakan teks. Waktu respons juga sebaiknya tidak berbeda jauh antara email yang ada dan yang tidak ada, karena perbedaan waktu dapat menjadi petunjuk lain.

Gunakan token acak, bukan informasi yang mudah ditebak

Token reset adalah kode sementara yang membuktikan bahwa seseorang memiliki akses ke kanal pemulihan, biasanya email. Token yang baik harus dibuat dengan generator acak yang aman secara kriptografis, cukup panjang, terikat ke satu akun, memiliki masa berlaku, dan hanya bisa dipakai sekali.

Hindari membuat token dari hal-hal seperti ID pengguna, timestamp, alamat email, atau gabungan data yang bisa ditebak. Hindari juga token pendek seperti enam digit jika tidak ada pembatasan percobaan yang ketat. Untuk tautan reset berbasis URL, gunakan nilai acak dari fungsi yang memang dirancang untuk kebutuhan keamanan.

$token = bin2hex(random_bytes(32));
$tokenHash = hash('sha256', $token);

// Simpan tokenHash, bukan token asli
// Sertakan user_id dan expiry_time di database

Token asli dikirim melalui email, sedangkan yang disimpan di database adalah hasil hash-nya. Dengan begitu, jika tabel token bocor, penyerang tidak langsung memperoleh tautan reset yang masih aktif. Saat token diterima, hash token tersebut lalu dibandingkan dengan nilai di database.

Pola ini bukan pengganti perlindungan lain. Tetap tambahkan masa berlaku, misalnya beberapa puluh menit sesuai kebutuhan aplikasi, dan hapus atau tandai token sebagai sudah digunakan setelah password berhasil diganti.

Jangan mengubah akun hanya karena pengguna meminta reset

Permintaan reset password bukan bukti bahwa peminta adalah pemilik akun. Pada tahap pertama, sistem hanya boleh mencatat permintaan dan mengirim instruksi ke kanal pemulihan yang sudah terdaftar.

Jangan mengunci akun hanya karena seseorang berkali-kali meminta reset. Langkah tersebut dapat disalahgunakan untuk membuat akun orang lain tidak bisa dipakai. Sebagai gantinya, terapkan pembatasan frekuensi berdasarkan kombinasi seperti alamat akun, alamat IP, dan perangkat. Pembatasan ini perlu dirancang hati-hati agar tidak mudah dilewati hanya dengan mengganti IP.

OWASP juga merekomendasikan agar sistem tidak otomatis mengubah password atau status akun sebelum token yang valid diberikan. Ini terdengar jelas, tetapi penting ketika alur reset melibatkan beberapa endpoint atau layanan terpisah.

Perlakukan halaman reset sebagai area sensitif

Setelah pengguna membuka tautan, jangan langsung menganggap seluruh browser sudah terautentikasi. Buat sesi terbatas yang hanya dapat digunakan untuk mengganti password. Sesi tersebut tidak boleh memberi akses ke dashboard, data pesanan, atau pengaturan akun lainnya.

Gunakan HTTPS untuk seluruh proses, termasuk saat membuka halaman reset. Tautan sebaiknya mengarah ke domain yang sudah ditentukan, bukan dibentuk begitu saja dari nilai Host pada request. Ini membantu menghindari pembuatan tautan berbahaya akibat manipulasi header.

Perhatikan pula kebocoran token melalui referrer, log server, analitik, atau URL yang dibagikan ulang. Setelah token diterima, aplikasi dapat memindahkan nilainya ke sesi terbatas dan menghapus token dari alamat browser. Pada halaman reset, kebijakan referrer yang ketat juga dapat membantu mengurangi kebocoran ke situs lain.

Password baru harus disimpan seperti password login

Password baru tidak boleh disimpan dalam bentuk teks biasa, dienkripsi dengan kunci yang mudah ditemukan, atau di-hash memakai SHA-256 secara langsung. Password memerlukan algoritma hashing adaptif yang sengaja dibuat lebih lambat, seperti Argon2id, bcrypt, atau PBKDF2.

Di PHP, gunakan API bawaan agar proses salt dan format hash ditangani dengan benar:

$hash = password_hash($newPassword, PASSWORD_DEFAULT);

if (password_verify($newPassword, $hash)) {
    // Password cocok dengan hash yang tersimpan
}

password_verify() dirancang untuk memeriksa kecocokan hash tanpa perlu membuat perbandingan manual yang rawan kesalahan. Jangan membuat salt sendiri atau menyimpan password asli “sementara” untuk keperluan debugging.

Soal aturan password, jangan hanya memaksa kombinasi simbol, huruf besar, dan angka tanpa mempertimbangkan panjang serta password yang sudah umum atau pernah bocor. Lebih berguna jika aplikasi menerima password panjang, tidak memotongnya secara diam-diam, dan menolak password yang masuk daftar umum atau berisiko.

Putuskan sesi lama setelah password diganti

Password reset seharusnya menjadi kesempatan untuk memutus akses yang mungkin sudah dimiliki penyerang. Setelah password berhasil diubah, invalidasi sesi lama, token reset lain yang masih aktif, dan—jika relevan—perangkat atau token autentikasi yang dicurigai.

Pengguna juga perlu menerima pemberitahuan bahwa password telah diubah. Notifikasi ini tidak boleh berisi password baru. Isinya cukup mencantumkan waktu perubahan, tindakan yang dapat dilakukan jika perubahan bukan dilakukan oleh pengguna, serta kanal bantuan resmi.

Untuk akun dengan MFA, password reset tidak otomatis menyelesaikan seluruh masalah. Jika penyerang juga mengubah email pemulihan, nomor telepon, atau metode MFA, proses pemulihan perlu memeriksa faktor lain yang sudah dipercaya. Jangan memulihkan semua informasi keamanan berdasarkan alamat email baru yang baru saja ditambahkan.

Checklist yang bisa diterapkan sekarang

  1. Gunakan pesan yang sama untuk email terdaftar dan tidak terdaftar.
  2. Buat token dengan generator acak yang aman, lalu simpan hash token di database.
  3. Berikan masa berlaku dan jadikan token sekali pakai.
  4. Terapkan rate limit pada permintaan reset dan percobaan penggunaan token.
  5. Gunakan HTTPS dan domain reset yang telah ditentukan.
  6. Buat sesi reset terbatas, bukan langsung login penuh.
  7. Simpan password dengan password_hash() dan verifikasi dengan password_verify().
  8. Invalidasi sesi lama dan token lain setelah password diganti.
  9. Kirim notifikasi perubahan password tanpa pernah mengirim password dalam email.

Apa artinya bagi kita?

Password reset yang aman bukan berarti membuat pengguna melewati banyak rintangan. Tujuannya adalah memastikan bahwa orang yang meminta perubahan benar-benar menguasai kanal pemulihan, sementara penyerang tidak mendapat petunjuk tambahan dari respons aplikasi.

Secara praktis, perbaikan paling bernilai biasanya bukan menambah fitur rumit, melainkan menutup detail yang sering diabaikan: pesan error yang terlalu jujur, token yang tidak kedaluwarsa, sesi lama yang tetap aktif, dan password yang disimpan dengan cara yang salah. Jalur pemulihan adalah bagian dari sistem autentikasi—jadi ia perlu diuji dan diaudit dengan keseriusan yang sama seperti halaman login.

Sumber & bacaan lebih lanjut

Jelajahi juga

– Rio Yotto @rioyotto