# Laravel Weak Password Hashing: How to Replace md5 and sha1 with bcrypt

> Using md5() or sha1() for password hashing is trivially crackable. Laravel uses bcrypt by default — make sure your application does too.

**Severity:** high | **Category:** Application Security

---

## The Issue

MD5 and SHA-1 are fast hash functions designed for data integrity, not password storage. Modern GPUs can compute billions of MD5 hashes per second, making brute-force attacks trivial. A database of MD5-hashed passwords can be cracked in minutes using rainbow tables. Laravel uses bcrypt (or argon2) by default through Hash::make(), but legacy code or custom authentication may bypass this. Any password hashed with md5() or sha1() should be considered compromised.

## Steps to Fix

### 1. Find all weak hashing in your codebase

Search for md5 and sha1 used in authentication or password contexts:

grep -rn 'md5(\|sha1(' app/ --include='*.php'

Look specifically for:
- md5($password) or sha1($password)
- Custom authentication that compares md5 hashes
- Legacy database columns with 32-character (md5) or 40-character (sha1) hashes

Note: md5() used for cache keys, ETags, or non-security purposes is acceptable.

### 2. Replace with Hash::make() and Hash::check()

Use Laravel's built-in hashing:

use Illuminate\Support\Facades\Hash;

// Hashing a password
$hashed = Hash::make($password);

// Verifying a password
if (Hash::check($plaintext, $hashed)) {
    // Password matches
}

// In a User model / registration
User::create([
    'password' => Hash::make($request->password),
]);

Laravel's Hash facade uses bcrypt by default with automatic salt generation. You can configure the algorithm in config/hashing.php.

### 3. Migrate existing md5/sha1 passwords

For existing users with legacy hashes, rehash on login:

// In your LoginController or custom auth logic
if ($user && md5($password) === $user->password) {
    // Legacy hash matches — rehash with bcrypt
    $user->update([
        'password' => Hash::make($password),
    ]);
    Auth::login($user);
}

After a reasonable migration period, force remaining users to reset their passwords.

## Verification

Check your database for short hash values (md5 = 32 chars, sha1 = 40 chars, bcrypt = 60 chars):

SELECT id, LENGTH(password) as hash_length FROM users WHERE LENGTH(password) < 60;

Any results indicate weak hashing. Run php artisan stackshield:scan --check=SS042 to scan for md5/sha1 usage in code.

## Prevention

Never use md5() or sha1() for passwords. Always use Hash::make(). Configure bcrypt rounds in config/hashing.php (default 12 is good). Consider upgrading to argon2id for new applications. Use StackShield to scan for weak hashing patterns.

---

## Frequently Asked Questions

### Is sha256 safe for password hashing?

No. SHA-256 is faster than md5/sha1 but still a fast hash function. Password hashing requires intentionally slow algorithms like bcrypt, argon2, or scrypt that make brute-force attacks computationally expensive. Use Hash::make() which handles this correctly.

### What bcrypt rounds should I use?

Laravel defaults to 12 rounds, which takes about 250ms to hash. This is a good balance between security and performance. Increase to 13-14 if your server can handle it. You can set this in config/hashing.php or .env with BCRYPT_ROUNDS.

### What is password peppering and should I implement it in Laravel?

A pepper is a secret value added to passwords before hashing, stored in application configuration (not the database). Unlike salts (which are stored with the hash), an attacker who steals only the database cannot crack peppered hashes without also compromising the pepper. Add it manually: Hash::make($password . config("app.pepper")). It adds a useful layer but is not a substitute for bcrypt with sufficient rounds.

### How do I force users with legacy MD5 password hashes to reset their passwords?

Add a password_version column to your users table. On login, detect legacy hashes by their length (strlen($user->password) === 32 for MD5, 40 for SHA-1) and either rehash immediately if the password matches, or set a flag that forces a password reset on the next login attempt. After a migration period, force a reset for all remaining users with legacy hash formats.

### Does Laravel's Hash::needsRehash() detect MD5 hashes?

No. Hash::needsRehash() detects bcrypt hashes created with a different cost factor than your current configuration. It does not recognize MD5 or SHA-1 as needing rehash — it only works on hashes that bcrypt can identify as its own format. You need custom detection logic based on hash length or a password_version column to identify and migrate legacy hash formats.

