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.
Password hashing is a one-way transformation that converts a plaintext password into a fixed-length string that cannot practically be reversed. The critical distinction is between cryptographic hash functions (MD5, SHA-1, SHA-256) designed for fast data integrity verification, and password hashing algorithms (bcrypt, argon2, scrypt) designed to be computationally expensive. MD5 can compute 10 billion hashes per second on modern GPUs. Bcrypt with 12 rounds computes roughly 4,000 hashes per second on the same hardware — a difference that makes brute-forcing impractical.
When attackers obtain a database of MD5-hashed passwords, they run the hash list against precomputed rainbow tables — dictionaries of common passwords with their MD5 hashes. A 10 GB rainbow table covers virtually every password under 8 characters. With modern GPU-based hashcat tools, attackers can crack MD5 hashes of most real-world passwords in minutes to hours, regardless of whether the passwords were salted. Bcrypt hashes from the same dump would take centuries with the same hardware.
Real-world impact is documented extensively. Breaches of LinkedIn (2012, 117 million SHA-1 hashes) and Adobe (2013, 153 million poorly-encrypted passwords) gave attackers billions of credentials to use in credential stuffing attacks against other services. Since users reuse passwords, a compromised MD5 hash database cascades across every service those users have accounts on. Laravel prevents this by defaulting to bcrypt through Hash::make(), but legacy code and custom authentication bypass this protection.
The Problem
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.
How 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.
How to Verify
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.
Related Security Terms
Related Guides
Laravel Session Security: Fix Insecure Cookie Config (Secure, HttpOnly, SameSite)
Laravel session cookies missing Secure, HttpOnly, or SameSite flags? Fix your config/session.php to prevent session hijacking, cookie theft, and CSRF attacks.
Laravel APP_KEY Security: How to Generate, Rotate, and Protect Your Encryption Key
A missing, short, or committed APP_KEY compromises session encryption, signed URLs, and all data encrypted with Crypt. Generate a strong key and keep it out of Git.
Hardcoded Credentials in Laravel: How to Find and Remove Secrets from Source Code
API keys, passwords, and secrets committed to source code are exposed to anyone with repository access. Move them to environment variables before they leak.
Is your Laravel app exposed right now?
34% of Laravel apps we scan have at least one critical issue. Most teams don't find out until something breaks. Our free scan checks your live application in under 60 seconds.