Skip to content

[SECURITY] Enforce Strong Password Policy di Seluruh Fitur (Change/Reset/Registration)#979

Merged
vickyrolanda merged 6 commits intorilis-devfrom
dev-963
Mar 30, 2026
Merged

[SECURITY] Enforce Strong Password Policy di Seluruh Fitur (Change/Reset/Registration)#979
vickyrolanda merged 6 commits intorilis-devfrom
dev-963

Conversation

@pandigresik
Copy link
Copy Markdown
Contributor

@pandigresik pandigresik commented Mar 11, 2026

Perbaikan issue #963

Password Policy Implementation

Ringkasan

Implementasi password policy yang kuat untuk meningkatkan keamanan aplikasi dengan fitur:

  • Validasi password kuat (minimal 12 karakter, kompleksitas tinggi)
  • Pengecekan HIBP (Have I Been Pwned) database
  • Password history untuk mencegah reuse
  • Password expiry dengan konfigurasi fleksibel
  • Audit script untuk user existing dengan password lemah

Fitur

1. StrongPassword Rule (app/Rules/StrongPassword.php)

Validasi password dengan kriteria:

  • Panjang minimal: 12 karakter (dapat dikonfigurasi)
  • Kompleksitas: Harus mengandung:
    • Huruf kapital (A-Z)
    • Huruf kecil (a-z)
    • Angka (0-9)
    • Karakter spesial (!@#$%^&*...)
  • HIBP Check: Memeriksa terhadap database password yang pernah bocor
  • Password History: Mencegah penggunaan password yang sama (5 terakhir)
  • Weak Pattern Detection: Menolak pola berulang atau berurutan
  • Common Passwords: Menolak password umum/terkenal

2. Password History (app/Models/PasswordHistory.php)

Menyimpan riwayat password user untuk:

  • Mencegah reuse password
  • Audit trail perubahan password
  • Tracking alasan perubahan (password_change, admin_reset, forced_reset, security_audit)

3. Password Expiry

  • Password dapat di-set untuk expire setelah periode tertentu (default: 90 hari)
  • User akan dipaksa reset password saat login jika password expired
  • Middleware CheckPasswordExpiry menangani redirect otomatis

4. Force Password Reset

  • User dengan password lemah dapat ditandai untuk forced reset
  • Halaman khusus untuk reset password wajib
  • Redirect otomatis saat mencoba akses aplikasi

Konfigurasi

File konfigurasi: config/password.php

return [
    'min_length' => 12,              // Panjang minimal password
    'expiry_days' => 90,             // Hari sebelum password expired (null = disabled)
    'history_count' => 5,            // Jumlah password history yang disimpan
    'check_hibp' => true,            // Enable HIBP check
    'force_reset_weak_passwords' => true,  // Force reset untuk password lemah
    'weak_patterns' => [...],        // Pattern password lemah
    'common_passwords' => [...],     // List password umum yang ditolak
];

Penggunaan

Registration

Password baru saat registrasi otomatis divalidasi dengan StrongPassword rule:

// app/Http/Requests/RegisterRequest.php
public function rules(): array
{
    return [
        'password' => ['required', 'string', 'confirmed', new StrongPassword()],
    ];
}

Change Password

User yang ingin ganti password harus memenuhi kriteria strong password:

// app/Http/Requests/ChangePasswordRequest.php
public function rules(): array
{
    return [
        'password_old' => ['required', new MatchOldPassword],
        'password' => ['required', 'string', 'confirmed', new StrongPassword()],
    ];
}

Reset Password

Reset password via email juga menggunakan validasi yang sama:

// app/Http/Requests/ResetPasswordRequest.php
public function rules(): array
{
    return [
        'password' => ['required', 'string', 'confirmed', new StrongPassword()],
    ];
}

Audit Script

Jalankan audit untuk mendeteksi user dengan password lemah:

# Dry run (preview saja)
php artisan password:audit-weak --dry-run

# Audit dengan konfirmasi
php artisan password:audit-weak

# Force tanpa konfirmasi
php artisan password:audit-weak --force

Script akan:

  1. Memeriksa semua user
  2. Mendeteksi password lemah (common passwords, hash lama, dll)
  3. Menandai user untuk forced password reset
  4. Mencatat ke password history

Middleware

CheckPasswordExpiry

Middleware ini memastikan user dengan password expired atau force_reset flag akan diarahkan ke halaman reset password.

Tambahkan ke route group:

Route::middleware(['auth', 'password.expiry'])->group(function () {
    // Routes yang memerlukan pengecekan password expiry
});

Model Methods (User)

// Cek apakah password expired
$user->isPasswordExpired();

// Cek apakah user perlu reset password
$user->requiresPasswordReset();

// Set password dengan history dan expiry
$user->setPasswordWithHistory($password, 'password_change', 90);

// Force user untuk reset password
$user->forcePasswordReset('security_audit');

Testing

Jalankan test untuk validasi password rule:

php artisan test --filter StrongPasswordRuleTest

Test mencakup:

  • Password kuat (pass)
  • Password terlalu pendek (fail)
  • Password tanpa huruf kapital (fail)
  • Password tanpa huruf kecil (fail)
  • Password tanpa angka (fail)
  • Password tanpa karakter spesial (fail)
  • Common passwords (fail)
  • Weak patterns (fail)
  • Password history (fail untuk password yang pernah digunakan)

Database Schema

password_histories table

- id (bigint, primary key)
- user_id (bigint, foreign key to users)
- password (string, hashed)
- reason (string: password_change, admin_reset, forced_reset, security_audit)
- created_at, updated_at

users table (added columns)

- password_expires_at (timestamp, nullable)
- force_password_reset (boolean, default false)

Error Messages

Validasi password menyediakan pesan error yang jelas:

  • Length: "Password harus memiliki minimal 12 karakter."
  • Complexity: "Password harus mengandung huruf kapital, huruf kecil, angka, dan karakter spesial (!@#$%^&*...)."
  • HIBP: "Password ini telah bocor di database password yang pernah diretas. Silakan gunakan password lain yang lebih unik."
  • History: "Password ini telah digunakan sebelumnya. Silakan gunakan password baru."
  • Weak Pattern: "Password terlalu lemah atau mudah ditebak. Hindari pola berulang atau berurutan."
  • Common: "Password ini terlalu umum dan mudah ditebak. Silakan gunakan password yang lebih unik."

File yang Dibuat/Dimodifikasi

Baru

  • app/Rules/StrongPassword.php
  • app/Models/PasswordHistory.php
  • app/Http/Requests/RegisterRequest.php
  • app/Http/Requests/ChangePasswordRequest.php
  • app/Http/Requests/ResetPasswordRequest.php
  • app/Http/Requests/ForcePasswordResetRequest.php
  • app/Http/Controllers/ForcePasswordResetController.php
  • app/Http/Middleware/CheckPasswordExpiry.php
  • app/Console/Commands/AuditWeakPasswords.php
  • config/password.php
  • resources/views/auth/force-password-reset.blade.php
  • database/migrations/2026_03_11_000001_create_password_histories_table.php
  • database/migrations/2026_03_11_091131_add_password_expires_at_to_users_table.php
  • tests/Feature/StrongPasswordRuleTest.php

Dimodifikasi

  • app/Models/User.php - Added relationships and password methods
  • app/Http/Kernel.php - Registered password.expiry middleware
  • routes/web.php - Added force password reset routes
  • app/Http/Controllers/Auth/RegisterController.php - Use FormRequest
  • app/Http/Controllers/Auth/ChangePasswordController.php - Use FormRequest, password history
  • app/Http/Controllers/Auth/ResetPasswordController.php - Use FormRequest, password history

Langkah Penyelesaian

  1. ✅ Buat custom Rule/Validation untuk password kuat
  2. ✅ Terapkan di seluruh proses registrasi, ganti password, reset password
  3. ✅ Implementasi password history dan expiry
  4. ✅ Audit script untuk user existing dengan password lemah
  5. ✅ Testing dengan berbagai kombinasi input

Setelah Perbaikan

  • Password lemah ditolak saat registrasi, ganti password, dan reset password
  • User existing dengan password lemah dapat diidentifikasi dan dipaksa reset
  • Password yang pernah bocor di HIBP database tidak dapat digunakan
  • Password reuse dicegah dengan history mechanism
  • Password expiry dapat dikonfigurasi sesuai kebutuhan compliance
simplescreenrecorder-2026-03-11_11.35.19.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants