Back to Read More
LaravelPHP

Laravel Hashing

Nov 22, 2025

Hashing is a one-way process that turns a password (or any data) into a fixed-length scrambled string. Unlike encryption, you cannot reverse a hash β€” you can only check if a given value matches the hash. This is exactly what you want for passwords: store the hash, and verify against it on login.

Why Hash Passwords?

Never do this
// NEVER store passwords in plain text!
$user->password = 'secret123';  // Anyone with DB access can read it
$user->save();
Always do this
// Hash the password β€” it becomes unreadable
$user->password = Hash::make('secret123');
$user->save();
// Stored as: "$2y$12$K4Iu6q7cW8e..."  (nobody can read the original)

If your database is ever compromised, hackers get useless hash strings instead of real passwords.

1. Bcrypt (Default)

Laravel uses Bcrypt by default. It's slow on purpose β€” this makes brute-force attacks impractical.

Using Hash facade
use Illuminate\Support\Facades\Hash;

// Create a hash
$hashed = Hash::make('my-password');
// Result: "$2y$12$K4Iu6q7cW8e..."

// Each call produces a DIFFERENT hash (due to random salt)
Hash::make('my-password');  // "$2y$12$abc..."
Hash::make('my-password');  // "$2y$12$xyz..."
// Both are valid hashes of the same password!

// Verify a password against a hash
if (Hash::check('my-password', $hashed)) {
    // Password is correct!
}

if (! Hash::check('wrong-password', $hashed)) {
    // Password is wrong
}
Why different hashes? Bcrypt adds a random "salt" each time. This prevents attackers from using pre-computed hash tables (rainbow tables). Both hashes still verify correctly against the original password.

2. Configuring Bcrypt Rounds

"Rounds" control how slow hashing is. More rounds = more secure but slower. Default is 12.

config/hashing.php
'bcrypt' => [
    'rounds' => env('BCRYPT_ROUNDS', 12),  // Default: 12
],
Custom rounds per hash
// Override rounds for a specific hash
$hashed = Hash::make('password', [
    'rounds' => 14,  // Slower but more secure
]);
RoundsSpeedUse case
10~65msTesting / development
12~250msDefault β€” good balance
14~1sHigh security applications

3. Argon2 (Alternative)

Argon2 is a newer algorithm that won the Password Hashing Competition. It's more resistant to GPU-based attacks.

config/hashing.php
// Switch to Argon2
'driver' => 'argon2id',  // or 'argon2i'

'argon' => [
    'memory'  => 65536,  // Memory cost in KiB (64MB)
    'threads' => 1,      // Number of threads
    'time'    => 4,      // Number of iterations
],
Usage (same API!)
// The API stays the same regardless of driver
$hashed = Hash::make('password');  // Uses Argon2 if configured
Hash::check('password', $hashed); // Works the same way

4. Auto-Rehashing

If you change your hashing configuration (e.g., increase rounds), old hashes still work. Laravel can automatically rehash on login:

Check if rehash is needed
use Illuminate\Support\Facades\Hash;

// Check if a hash needs to be rehashed (config changed)
if (Hash::needsRehash($user->password)) {
    $user->update([
        'password' => Hash::make($plainPassword),
    ]);
}
Common pattern in login
public function login(Request $request)
{
    $credentials = $request->validate([
        'email' => 'required|email',
        'password' => 'required',
    ]);

    if (Auth::attempt($credentials)) {
        // Auto-rehash if config changed
        if (Hash::needsRehash(Auth::user()->password)) {
            Auth::user()->update([
                'password' => Hash::make($request->password),
            ]);
        }

        return redirect('/dashboard');
    }

    return back()->withErrors(['email' => 'Invalid credentials']);
}

5. Common Patterns

Registration
User::create([
    'name'     => $request->name,
    'email'    => $request->email,
    'password' => Hash::make($request->password),
]);
Change Password
public function changePassword(Request $request)
{
    $request->validate([
        'current_password' => 'required',
        'new_password'     => 'required|min:8|confirmed',
    ]);

    // Verify current password
    if (! Hash::check($request->current_password, auth()->user()->password)) {
        return back()->withErrors(['current_password' => 'Current password is incorrect']);
    }

    // Update with new hash
    auth()->user()->update([
        'password' => Hash::make($request->new_password),
    ]);

    return back()->with('success', 'Password changed!');
}
Auto-hash with Eloquent mutator
// app/Models/User.php
use Illuminate\Database\Eloquent\Casts\Attribute;

protected function password(): Attribute
{
    return Attribute::make(
        set: fn (string $value) => Hash::make($value),
    );
}

// Now you can just do:
$user->password = 'plain-text';  // Auto-hashed before saving!

Summary

  • βœ“One-way β€” hashes cannot be reversed (unlike encryption)
  • βœ“Hash::make() β€” create a hash from a password
  • βœ“Hash::check() β€” verify a password matches a hash
  • βœ“Bcrypt β€” default, 12 rounds, good for most apps
  • βœ“Argon2 β€” alternative, more resistant to GPU attacks
  • βœ“needsRehash() β€” auto-upgrade hashes when config changes

Β© 2026 Koeuk KOS. All rights reserved.

Built with Nuxt.js, Vue.js & Tailwind CSS