Password Security Analyzer
| Analyzer ID | Category | Severity | Time To Fix |
|---|---|---|---|
password-security | 🛡️ Security | Critical | 20 minutes |
What This Checks
Validates password hashing configuration, code-level hashing practices, password policy enforcement, and rehash usage across your Laravel application:
- Weak hashing drivers — MD5, SHA1, or SHA256 configured in
config/hashing.php - Insufficient bcrypt/Argon2 parameters — bcrypt rounds below 12, weak Argon2 memory/time/threads
- Weak hashing in code —
md5(),sha1(),hash(), orpassword_hash()with weak algorithms on password arguments - Plain-text password storage — password assignments and Eloquent/DB calls that store unhashed input
- Missing password policy — no
Password::defaults()in service providers, or missing length/complexity/breach checks - Weak validation rules — password fields with minimum length below 8 in Form Requests or Controllers
- Long confirmation timeout —
password_timeoutinconfig/auth.phpexceeding 3 hours - Missing rehash on login — no
Hash::needsRehash()usage orrehash_on_logindisabled in hashing config
Why It Matters
- Breach Impact: Weak hashing lets attackers crack passwords in seconds — LinkedIn (2012) saw 90% of 6.5 M SHA1 hashes cracked in hours
- Plain-Text Catastrophe: Storing raw passwords means a single database leak exposes every credential with zero effort
- Weak Policies: Short or simple passwords are the first to fall in credential-stuffing and dictionary attacks
- Compliance: OWASP, PCI DSS, and NIST SP 800-63B all mandate strong hashing (bcrypt 12+/Argon2id) and minimum password complexity
- Rehash Gap: When you increase bcrypt rounds from 10 to 12, existing users keep the weaker hash unless passwords are rehashed on login
How to Fix
Quick Fix (5 minutes)
Scenario 1: Weak Bcrypt Rounds
// config/hashing.php
'bcrypt' => [
'rounds' => 12, // Minimum recommended; default 10 is weak
],Scenario 2: Using MD5/SHA1 in Code
// BEFORE
$user->password = md5($request->password);
// AFTER
use Illuminate\Support\Facades\Hash;
$user->password = Hash::make($request->password);Scenario 3: Plain-Text Storage in Eloquent Calls
// BEFORE
User::create([
'password' => $request->password, // plain text!
]);
// AFTER
User::create([
'password' => Hash::make($request->password),
]);Scenario 4: No Password Policy
// app/Providers/AppServiceProvider.php
use Illuminate\Validation\Rules\Password;
public function boot(): void
{
Password::defaults(function () {
return Password::min(8)
->letters()
->mixedCase()
->numbers()
->symbols()
->uncompromised();
});
}Scenario 5: Weak Validation Rules
// BEFORE
'password' => 'required|min:4'
// AFTER
'password' => ['required', Password::defaults()]Scenario 6: Enable Rehash on Login (Laravel 11+)
// config/hashing.php
'rehash_on_login' => true,Proper Fix (20 minutes)
Implement comprehensive password security across your application:
1. Configure Strong Hashing
// config/hashing.php
return [
'driver' => 'argon2id', // Or 'bcrypt' for broader compatibility
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 12),
],
'argon' => [
'memory' => 65536, // 64 MB - resists GPU attacks
'time' => 2,
'threads' => 2,
],
'rehash_on_login' => true, // Laravel 11+
];2. Replace All Weak Hashing
use Illuminate\Support\Facades\Hash;
// Registration / password updates
$user->password = Hash::make($request->password);
// Verification
if (Hash::check($request->password, $user->password)) {
// Authenticated
}3. Set Password Defaults
// app/Providers/AppServiceProvider.php
Password::defaults(function () {
return Password::min(8)
->letters()
->mixedCase()
->numbers()
->symbols()
->uncompromised();
});4. Use Defaults in Validation
// In Form Requests or Controllers
'password' => ['required', 'confirmed', Password::defaults()],5. Reduce Confirmation Timeout
// config/auth.php
'password_timeout' => 10800, // 3 hours (default); reduce if appropriate6. Rehash on Login (Pre-Laravel 11)
if (Hash::check($request->password, $user->password)) {
if (Hash::needsRehash($user->password)) {
$user->password = Hash::make($request->password);
$user->save();
}
Auth::login($user);
}7. Configure Legitimate MD5 Usage
If you use MD5/SHA1 for non-password purposes (cache keys, checksums, ETags):
// config/shieldci.php
'password_security' => [
'allowed_weak_hash_patterns' => [
'cache',
'fingerprint',
'checksum',
'etag',
],
],TIP
MD5 and SHA1 are acceptable for non-cryptographic purposes like cache keys and checksums. The analyzer only flags them when used on password-related arguments.
ShieldCI Configuration
This analyzer is automatically skipped in CI environments ($runInCI = false) because hashing configuration is environment-specific.
Configuration is read from the password_security key in config/shieldci.php.
// config/shieldci.php
'password_security' => [
// Hashing thresholds
'bcrypt_min_rounds' => 12,
'argon2_min_memory' => 65536,
'argon2_min_time' => 2,
'argon2_min_threads' => 2,
// Confirmation timeout ceiling (seconds)
'password_confirmation_max_timeout' => 10800, // 3 hours
// Allow weak hashing for non-password use cases
'allowed_weak_hash_patterns' => [
'cache',
'fingerprint',
'checksum',
'etag',
],
// Ignore specific files from code scanning
'ignored_paths' => [
'app/Utilities/CacheKeyGenerator.php',
],
],References
- Laravel Hashing Documentation
- Laravel Password Validation
- OWASP Password Storage Cheat Sheet
- NIST Digital Identity Guidelines (SP 800-63B)
- PHP password_hash() Manual
Related Analyzers
- Authentication & Authorization Analyzer - Validates authentication implementation
- Login Throttling Analyzer - Prevents brute force attacks on login endpoints
- Cookie Analyzer - Validates session cookie security configuration
- Debug Mode Analyzer - Ensures debug mode disabled in production