Password hashing itu soal menyimpan password tanpa pernah menyimpan password aslinya. Bayangin kamu mau memverifikasi kunci rumah seseorang tanpa menyimpan kuncinya: kamu simpan foto pola giginya saja. Dari foto itu kamu bisa mencocokkan kunci yang datang, tapi kamu tidak bisa membentuk ulang kuncinya dari foto. Hash adalah foto pola gigi itu: sidik jari satu arah dari password.
🧪 Tebakan cepat: dua orang kebetulan pakai password yang sama,
password123. Kalau password mereka di-hash pakai SHA-256, hash-nya sama atau beda? Tebak dulu, jawabannya ada di bagian "Kenapa hash cepat itu salah".
Jangan simpan plain text
Kalau database bocor dan password tersimpan apa adanya (plain text), semua akun langsung terekspos. Lebih parah lagi, banyak orang pakai password yang sama di banyak layanan, jadi satu kebocoran menyeret akun-akun lain. Maka password tidak pernah disimpan apa adanya. Yang disimpan adalah hash-nya.
Hash: sidik jari satu arah
Hash mengubah teks apa pun jadi deretan karakter dengan panjang tetap, dan prosesnya satu arah: gampang dari password ke hash, (nyaris) mustahil balik dari hash ke password. Waktu login, server meng-hash password yang kamu ketik, lalu membandingkan dengan hash yang tersimpan. Cocok berarti benar.
Kenapa hash cepat itu salah
Hash seperti MD5, SHA-1, dan SHA-256 dibuat untuk cepat. Untuk file itu bagus, untuk password itu bencana. Di Lab, hash rahasia123 dengan SHA-256:
SHA-256("rahasia123")
= bee5688aea66a47460b19c76f8f199c6b9585eb726f8322b1429793863609ca2
Hash yang sama persis muncul setiap kali, karena SHA-256 deterministik: input sama, output sama. Ada tiga masalah dari situ:
- Password sama menghasilkan hash sama.
password123milik dua orang menghasilkan hash yang identik (ef92b778...). Jadi jawaban tebakannya: sama. Penyerang yang melihat dua hash sama langsung tahu dua orang itu pakai password yang sama. - Rainbow table. Karena deterministik, penyerang bisa menyiapkan tabel raksasa berisi jutaan
password -> hashlebih dulu, lalu mencocokkan hash curian secara instan. - Brute force kilat. SHA-256 begitu cepat sehingga satu GPU modern bisa mencoba sekitar sepuluh miliar tebakan per detik. Password pendek jebol dalam hitungan detik.
Kesimpulannya: jangan pakai MD5, SHA-1, atau SHA-256 untuk password.
Salt: bikin tiap hash unik
Salt adalah string acak yang digabungkan ke password sebelum di-hash. Tiap user dapat salt berbeda, jadi:
password123+ salt A menghasilkan hash yang beda daripassword123+ salt B. Dua orang dengan password sama kini punya hash berbeda.- Rainbow table jadi sia-sia, karena tabel yang disiapkan tanpa salt tidak cocok lagi.
Salt tidak rahasia; dia disimpan bersama hash. Tujuannya bukan menyembunyikan, tapi membuat tiap hash unik.
bcrypt dan argon2id: sengaja lambat
bcrypt dan argon2id dirancang khusus untuk password, dengan dua sifat yang hilang di SHA-256:
- Salt otomatis. Salt dibuat dan disimpan di dalam hash itu sendiri, jadi kamu tidak perlu mengurusnya terpisah.
- Cost factor. Kamu bisa mengatur seberapa lambat proses hashing. Tiap kenaikan satu tingkat menggandakan kerja yang dibutuhkan. Lambat buat kamu cuma soal milidetik per login, tapi buat penyerang yang harus mencoba miliaran tebakan, itu memperlambatnya besar-besaran.
Hash-nya kelihatan begini (perhatikan formatnya, bukan nilainya):
bcrypt : $2b$12$R9h/cIPz0gi.URNNX3kh2OPST9/PgBkqquzi.Ss7KIUgO2t0jWMUW
argon2id : $argon2id$v=19$m=19456,t=2,p=1$c29tZXNhbHQ$RdescudvJCsgt3ub...
$2b$menandai bcrypt,12adalah cost factor, sisanya salt dan hash jadi satu.$argon2id$menandai argon2id.m=19456memori (sekitar 19 MiB),t=2jumlah iterasi,p=1paralelisme.
Rekomendasi OWASP saat ini: pakai argon2id (minimal memori 19 MiB, iterasi 2, paralelisme 1), atau bcrypt dengan cost minimal 10. Atur cost sampai satu kali hashing memakan waktu sekitar seperempat detik di servermu.
Di PHP: password_hash dan password_verify
Di latihan, PHP sudah menyediakan ini dengan aman:
$password = "rahasia123";
$hash = password_hash($password, PASSWORD_DEFAULT); // bcrypt, salt otomatis
echo "Hash berhasil dibuat\n";
// Verifikasi: cocokkan password mentah dengan hash tersimpan
echo "Password cocok: " . (password_verify("rahasia123", $hash) ? "true" : "false") . "\n";
echo "Password salah: " . (password_verify("password_salah", $hash) ? "true" : "false") . "\n";
password_hash($password, PASSWORD_DEFAULT)membuat hash dengan algoritma terbaik yang tersedia. Saat iniPASSWORD_DEFAULTberarti bcrypt. Itu jawaban kuisnya.password_verify($input, $hash)mencocokkan password mentah dengan hash. Jangan pernah membandingkan dua hash dengan tanda sama dengan, karena salt membuat hash yang sama pun tak akan identik.- Outputnya:
Hash berhasil dibuat, laluPassword cocok: true, laluPassword salah: false.
⚠️ Jebakan yang sering ketemu
- Pakai MD5 atau SHA untuk password. Terlalu cepat, tanpa salt, dan sudah banyak rainbow table-nya. Pakai
password_hash(). - Membandingkan hash sendiri.
if (md5($input) === $stored)itu salah dua kali: algoritmanya salah, dan verifikasinya harusnya pakaipassword_verify(). - Menyimpan salt rahasia terpisah dan menganggapnya kunci. Salt tidak rahasia dan sudah ikut di dalam hash bcrypt/argon2. Yang membuat lambat adalah cost factor, bukan menyembunyikan salt.
- Membatasi panjang password terlalu ketat. Izinkan password panjang (minimal 8, sampai 64 karakter atau lebih), jangan potong di tengah.
TL;DR: Password disimpan sebagai hash satu arah, bukan plain text. MD5/SHA-256 salah untuk password: terlalu cepat (sekitar sepuluh miliar tebakan per detik di GPU), deterministik (password sama = hash sama, jadi rainbow table jalan). Salt bikin tiap hash unik. bcrypt dan argon2id sengaja lambat lewat cost factor dan punya salt otomatis. Di PHP, password_hash($pw, PASSWORD_DEFAULT) (bcrypt) untuk membuat, password_verify($input, $hash) untuk mencocokkan. OWASP sekarang: argon2id (19 MiB, t=2, p=1) atau bcrypt cost minimal 10.