Menyimpan Password dengan Benar
Setiap kali saya audit sistem baru — entah itu aplikasi klinik, sistem internal, atau proyek sampingan — salah satu hal pertama yang saya cek adalah bagaimana password disimpan di database. Ini kedengarannya dasar, tapi saya masih sering menemukan kolom password yang ternyata cuma dienkripsi, bukan di-hash. Bedanya krusial, dan kalau salah paham, konsekuensinya bisa fatal saat database bocor.
Hashing vs Enkripsi: Kenapa Bedanya Penting
Enkripsi itu dua arah — ada kunci untuk mengenkripsi, ada kunci untuk mendekripsi balik ke teks asli. Kalau kunci itu bocor (dan biasanya kunci disimpan di tempat yang sama dengan aplikasi, misalnya file .env), semua password bisa dibuka kembali. Hashing itu satu arah. Fungsi hash mengubah input jadi output yang secara praktis tidak bisa dibalik ke bentuk asalnya.
Untuk password, kita tidak pernah butuh mengembalikan teks aslinya. Yang kita butuhkan cuma memverifikasi: apakah password yang dimasukkan user, kalau di-hash, hasilnya sama dengan hash yang tersimpan? Karena itu hashing adalah pilihan yang benar secara desain, bukan sekadar soal kekuatan algoritma.
Jebakan klasik yang sering saya temui: seseorang memakai AES atau bahkan base64 (yang bukan enkripsi sama sekali, cuma encoding) untuk "mengamankan" kolom password. Base64 bisa dibalik siapa saja dalam sepersekian detik tanpa kunci apa pun. AES butuh kunci, tapi kalau kunci itu ikut bocor bersama database (kasus umum kalau backup dan kredensial disimpan berdekatan), enkripsi jadi percuma.
Algoritma yang Tepat: Lambat Itu Fitur
Hash biasa seperti MD5 atau SHA-256 dirancang untuk cepat — itu bagus untuk checksum file, tapi buruk untuk password, karena penyerang bisa mencoba miliaran kombinasi per detik dengan GPU. Untuk password, kita justru butuh algoritma yang sengaja lambat dan memakan memori, seperti bcrypt, argon2, atau scrypt. Ini membuat serangan brute-force jadi mahal secara komputasi, bahkan kalau attacker sudah punya hash-nya.
Di Laravel, ini sudah dibungkus rapi lewat facade Hash, default-nya memakai bcrypt (atau argon2id kalau dikonfigurasi):
use Illuminate\Support\Facades\Hash;
$hashed = Hash::make($request->password);
// verifikasi saat login
if (Hash::check($request->password, $user->password)) {
// cocok
}Poin pentingnya: jangan pernah implementasi hashing sendiri. Salt, cost factor, dan cara membandingkan hash (harus constant-time comparison supaya tidak bocor lewat timing attack) sudah ditangani dengan benar oleh library yang matang. Menulis ulang logika ini sendiri, sekecil apa pun, adalah sumber bug keamanan yang sangat umum.
Jebakan Saat Migrasi Skema Lama
Yang paling sering bikin pusing bukan sistem baru, tapi sistem lama yang perlu dimigrasi. Saya sering menemui pola seperti ini di codebase warisan: password disimpan sebagai MD5(password) polos, atau lebih parah, MD5(password + salt_statis) dengan salt yang sama untuk semua user.
Beberapa jebakan yang perlu diwaspadai saat migrasi:
- Jangan langsung re-hash hash lama. Meng-hash ulang MD5 dengan bcrypt (misalnya
bcrypt(md5($password))) memang bisa jadi solusi sementara, tapi itu tetap mewarisi kelemahan MD5 di lapisan dalam — kalau MD5 tabrakan (collision) atau precomputed table cocok, lapisan luar tidak membantu banyak. Idealnya, hash lama hanya dipakai sekali untuk memverifikasi saat user login berikutnya, lalu segera diganti dengan hash password asli yang di-hash ulang dengan algoritma modern. - Strategi migrasi bertahap ("lazy rehash") lebih realistis daripada memaksa semua user reset password sekaligus. Saat user login dengan sukses, itulah momen kita punya akses ke password asli — di situ kita hash ulang dengan algoritma baru dan simpan, sambil menandai skema hash yang dipakai (misalnya kolom
password_algo) supaya proses verifikasi tahu cara memperlakukan hash lama vs baru. - Jangan lupa force logout semua sesi lama setelah migrasi besar, dan pertimbangkan reset token untuk user yang tidak login dalam waktu lama — karena mereka tidak akan pernah melewati proses lazy rehash di atas.
- Salt itu bukan rahasia yang perlu disembunyikan terpisah, tapi harus unik per user. Library modern seperti bcrypt sudah menyertakan salt di dalam string hash-nya sendiri, jadi tidak perlu kolom terpisah — ini sering bikin bingung tim yang terbiasa dengan skema manual lama.
Takeaway
Prinsipnya sederhana: password di-hash dengan algoritma yang memang dirancang lambat (bcrypt, argon2, scrypt), bukan dienkripsi dan bukan di-hash dengan algoritma cepat seperti MD5/SHA polos. Kalau mewarisi skema lama, migrasi bertahap lewat lazy rehash saat login jauh lebih aman dan realistis daripada mencoba konversi massal sekaligus. Dan yang paling penting — percayakan mekanismenya ke library yang sudah teruji, jangan menciptakan skema sendiri untuk sesuatu sekritis ini.