AlgoInsight

Q&A

How to strengthen password validations in Laravel

By AlgoInsight Team2026-06-18databaseslaravelsecurityphp

Overview

Weak passwords are a top credential-attack vector. Laravel's Illuminate\Validation\Rules\Password class (available since Laravel 9) makes it easy to enforce a layered policy in one readable chain - length, complexity, and a breach check - and works exactly like any other validation rule.

graph TD
    A["Password::min(8)"] --> B[".letters()"]
    B --> C[".mixedCase()"]
    C --> D[".numbers()"]
    D --> E[".symbols()"]
    E --> F[".uncompromised()"]
    F --> G[Validation passes / fails with messages]
    style A fill:#1e1b4b,color:#fff

The Password Rule Object (Laravel 9+)

use Illuminate\Validation\Rules\Password;

$request->validate([
    'password' => [
        'required',
        'confirmed',              // must match 'password_confirmation' field
        Password::min(8)          // at least 8 characters
            ->max(128)            // prevent excessive-length DoS
            ->letters()           // must contain letters
            ->mixedCase()         // uppercase AND lowercase
            ->numbers()           // at least one digit
            ->symbols()           // at least one symbol (!@#...)
            ->uncompromised(),    // not found in known breach databases
    ],
]);

Each method in the chain adds a requirement; all must pass. The automatic error messages are localization-friendly.

What Each Constraint Does

Constraint Requirement
min(n) Minimum length
max(n) Maximum length
letters() At least one letter
mixedCase() At least one uppercase + one lowercase
numbers() At least one digit
symbols() At least one special character
uncompromised($times) Not in Have I Been Pwned database (uses k-anonymity, privacy-safe)

Setting a Default Password Rule App-Wide

Define your standard rule once in AppServiceProvider and reference it everywhere:

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

public function boot(): void
{
    Password::defaults(fn () =>
        app()->environment('production')
            ? Password::min(8)->mixedCase()->numbers()->symbols()->uncompromised()
            : Password::min(8)    // relaxed in local dev for convenience
    );
}

Then in any request:

'password' => ['required', 'confirmed', Password::defaults()],

A single change in boot() applies site-wide - no hunting down every validation rule.

Custom Validation Rules

For organization-specific policies (e.g. no repeating characters, no username in password), add a custom rule:

use Illuminate\Validation\Rules\Password;
use Illuminate\Contracts\Validation\Rule;

class NotContainsUsername implements Rule
{
    public function passes($attribute, $value): bool
    {
        return !str_contains(strtolower($value), strtolower(auth()->user()->name));
    }
    public function message(): string { return 'Password must not contain your username.'; }
}

// In validation:
'password' => ['required', 'confirmed', Password::min(8)->symbols(), new NotContainsUsername],

Form Request (cleanest location for rules)

// app/Http/Requests/RegisterRequest.php
public function rules(): array
{
    return [
        'email'    => 'required|email|unique:users',
        'password' => ['required', 'confirmed', Password::defaults()],
    ];
}

Moving validation into a FormRequest class keeps controllers lean and rules testable.

Bcrypt / Hashing (side note)

Strong validation only matters if passwords are stored correctly. Always hash with:

bcrypt($request->password)
// or:
Hash::make($request->password)

Never store plaintext or MD5/SHA1 hashes. Laravel uses bcrypt (or Argon2 if configured) automatically.

Why This Matters in Interviews

Security interviews use this to assess real-world input validation knowledge. Cover: the Password rule object chain (min, mixedCase, numbers, symbols, uncompromised), setting app-wide defaults in AppServiceProvider, custom Rule classes for bespoke policies, confirmed for confirmation matching, and always hashing with bcrypt/Argon2. The uncompromised() k-anonymity breach check is a strong signal of security awareness.

Common Pitfalls

References

More answers