Password dan Login Aman di Laravel 11: Hashing hingga 2FA

Banyak aplikasi bisnis yang berjalan bertahun-tahun ternyata masih menyimpan password dengan MD5, atau lebih buruk lagi, dalam bentuk teks biasa. Selama database tidak bocor, semua...

Password dan Login Aman di Laravel 11: Hashing hingga 2FA

Banyak aplikasi bisnis yang berjalan bertahun-tahun ternyata masih menyimpan password dengan MD5, atau lebih buruk lagi, dalam bentuk teks biasa. Selama database tidak bocor, semuanya terasa baik-baik saja. Begitu ada backup yang tertinggal di hosting atau celah SQL injection, seluruh password pengguna bisa dibaca dalam hitungan menit. Karena banyak orang memakai password yang sama di beberapa tempat, kebocoran itu juga membuka akun email dan media sosial mereka.

Artikel ini membahas cara menyimpan password dengan benar, memigrasikan password lama tanpa memaksa semua pengguna reset, serta mengamankan alur login, logout, dan reset password di Laravel 11.

Kenapa MD5 dan SHA1 Tidak Cocok untuk Password

MD5 dan SHA1 dirancang untuk cepat. Untuk checksum file, itu kelebihan. Untuk password, itu kelemahan, karena kartu grafis biasa bisa mencoba miliaran tebakan MD5 per detik. Ditambah lagi, hash MD5 untuk password umum seperti 123456 sudah tersedia di tabel siap pakai.

Cara menyimpanPenilaianAlasan
Teks biasaTidak bolehSiapa pun yang membaca database langsung tahu password
MD5 / SHA1 / SHA256 tanpa saltTidak bolehTerlalu cepat dan rentan tabel hash siap pakai
Enkripsi (bisa didekripsi)Tidak bolehJika kunci bocor, semua password terbuka
bcrypt / Argon2idDisarankanSengaja lambat, memakai salt acak per password

Langkah 1: Hashing dengan Benar di Laravel

Laravel memakai bcrypt secara default. Di Laravel 11, model User bawaan sudah memiliki cast 'password' => 'hashed', sehingga password polos yang diisi ke model otomatis di-hash:

// app/Models/User.php (bawaan Laravel 11)
protected function casts(): array
{
    return [
        'email_verified_at' => 'datetime',
        'password'          => 'hashed',
    ];
}

// Saat registrasi — cukup isi password polos
User::create([
    'name'     => $data['name'],
    'email'    => $data['email'],
    'password' => $data['password'],
]);

// Saat memeriksa password secara manual
if (Hash::check($request->password, $user->password)) {
    // cocok
}

Tingkat kerumitan bcrypt diatur lewat BCRYPT_ROUNDS di .env (default 12). Laravel 11 juga otomatis memperbarui hash saat pengguna login bila nilai rounds dinaikkan, jadi Anda tidak perlu memigrasikannya manual.

Langkah 2: Migrasi Password MD5 Lama Tanpa Reset Massal

Jika Anda mewarisi aplikasi lama yang menyimpan MD5, memaksa semua pengguna reset password biasanya memicu banyak keluhan. Cara yang lebih halus adalah memigrasikan hash saat pengguna berhasil login:

public function store(Request $request)
{
    $credentials = $request->validate([
        'email'    => ['required', 'email'],
        'password' => ['required', 'string'],
    ]);

    $user = User::where('email', $credentials['email'])->first();

    // Hash MD5 selalu 32 karakter heksadesimal; hash bcrypt diawali $2y$
    if ($user && preg_match('/^[a-f0-9]{32}$/', $user->password)
        && hash_equals($user->password, md5($credentials['password']))) {
        $user->forceFill(['password' => $credentials['password']])->save(); // di-hash ulang oleh cast
    }

    if (! Auth::attempt($credentials, $request->boolean('remember'))) {
        return back()->withErrors(['email' => 'Email atau password salah.'])->onlyInput('email');
    }

    $request->session()->regenerate();
    return redirect()->intended('/dashboard');
}

Setelah beberapa bulan, cek berapa akun yang masih memakai MD5 dengan query SELECT COUNT(*) FROM users WHERE password NOT LIKE '$2y$%'. Akun yang tersisa biasanya sudah tidak aktif; kirimkan email reset password kepada mereka dan hapus kode migrasi di atas.

Langkah 3: Aturan Password yang Masuk Akal

Aturan yang terlalu rumit membuat pengguna menulis password di kertas, sedangkan aturan yang terlalu longgar mengizinkan 12345678. Laravel menyediakan rule Password yang bisa didefinisikan sekali untuk seluruh aplikasi:

// app/Providers/AppServiceProvider.php
use Illuminate\Validation\Rules\Password;

public function boot(): void
{
    Password::defaults(function () {
        return $this->app->isProduction()
            ? Password::min(10)->letters()->numbers()->uncompromised()
            : Password::min(8);
    });
}

// Di form registrasi atau ganti password
'password' => ['required', 'confirmed', Password::defaults()],

Aturan uncompromised() mengecek apakah password pernah muncul di kebocoran data publik lewat layanan Have I Been Pwned. Yang dikirim hanya potongan awal hash-nya (metode k-anonymity), bukan password itu sendiri. Karena butuh koneksi internet, aktifkan hanya di produksi seperti contoh di atas.

Langkah 4: Amankan Proses Login

  • Pesan error yang sama. Tampilkan "Email atau password salah" untuk kedua kasus, jangan "Email tidak terdaftar". Pesan yang berbeda memberi tahu penyerang email mana yang terdaftar.
  • Regenerasi session setelah login. Panggilan $request->session()->regenerate() pada contoh di atas mencegah session fixation, yaitu penyerang yang sudah menyiapkan ID session sebelum korban login.
  • Batasi percobaan login. Gunakan rate limiter seperti dijelaskan di checklist keamanan Laravel, misalnya lima percobaan per menit per kombinasi email dan IP.

Langkah 5: Logout yang Benar-Benar Mengakhiri Session

public function destroy(Request $request)
{
    Auth::guard('web')->logout();
    $request->session()->invalidate();      // hapus data session lama
    $request->session()->regenerateToken();  // token CSRF baru
    return redirect('/');
}

Untuk fitur "keluar dari semua perangkat lain" setelah ganti password, aktifkan middleware AuthenticateSession pada route yang login, lalu panggil Auth::logoutOtherDevices($request->password).

Langkah 6: Reset Password yang Aman

Fitur reset bawaan Laravel (dipakai oleh Breeze dan Fortify) sudah menangani hal-hal penting: token acak sekali pakai, masa berlaku token, dan penyimpanan token dalam bentuk hash. Pastikan konfigurasinya masuk akal:

// config/auth.php
'passwords' => [
    'users' => [
        'provider' => 'users',
        'table'    => 'password_reset_tokens',
        'expire'   => 60, // menit; jangan dibuat berhari-hari
        'throttle' => 60, // detik jeda sebelum boleh meminta tautan baru
    ],
],

Pada halaman "lupa password", tampilkan pesan yang sama apa pun hasilnya, misalnya "Jika email terdaftar, tautan reset sudah kami kirim". Setelah password berhasil diganti, kirim email pemberitahuan ke pemilik akun agar ia sadar bila perubahan itu bukan dilakukannya.

Langkah 7: Pertimbangkan Verifikasi Dua Langkah (2FA)

Untuk akun admin, kasir kepala, atau siapa pun yang bisa melihat data keuangan, password saja tidak cukup. Laravel Fortify menyediakan 2FA berbasis aplikasi authenticator (TOTP) seperti Google Authenticator, lengkap dengan kode pemulihan. Jika belum memungkinkan, minimal wajibkan 2FA untuk akun dengan peran tertinggi.

Checklist Autentikasi

ItemCara mengecek
Password disimpan dengan bcrypt/Argon2Kolom password diawali $2y$ atau $argon2id$
Tidak ada sisa hash MD5SELECT COUNT(*) FROM users WHERE password NOT LIKE '$2%' AND password NOT LIKE '$argon%'
Aturan password diterapkanCoba daftar dengan 12345678: harus ditolak
Pesan login tidak membedakan email dan passwordLogin dengan email tidak terdaftar dan password salah: pesannya sama
Session diregenerasi saat loginBandingkan cookie session sebelum dan sesudah login: harus berubah
Rate limit login aktifEnam kali salah dalam semenit: harus muncul 429
Token reset kedaluwarsaNilai expire di config/auth.php tidak lebih dari 60 menit

Keamanan login adalah satu bagian dari gambaran yang lebih besar. Untuk aplikasi yang juga menyediakan API, lanjutkan ke panduan keamanan API yang baik, dan untuk pengaturan peran pengguna lihat role dan permission dengan Spatie.

Ingin langsung mempraktikkannya?

Buat akun gratis untuk mengunduh contoh aplikasi Laravel, menyimpan tutorial favorit, dan mencoba kursus. Gratis, bisa daftar dengan akun Google.

Daftar Gratis Lihat katalog aplikasi
Yudhi
Ditulis oleh
Yudhi
Founder & Lead Developer, GudangCode

Yudhi adalah founder GudangCode dan developer Laravel dengan pengalaman membangun puluhan sistem informasi bisnis siap pakai — mulai dari POS, HRIS, hingga aplikasi manajemen. Ia menulis panduan dan artikel di GudangCode untuk membantu developer Indonesia menjalankan, memahami, dan men-deploy source code Laravel dengan benar.

Laravel PHP MySQL Sistem Informasi Bisnis
Lihat semua artikel Yudhi
Autentikasi Aman Keamanan & Best Practice
Bacaan Paling Dicari
📚 Free Learning Hub

Learn Coding for Free at DhieCoderWeb

Explore Laravel, PHP, JavaScript tutorials, source code, web development guides, and practical programming tips.

DhieCoderWeb
100+
Tutorials
Free
Learning
SEO
Tips
Visit Dhiecoderweb.com →