bordoni/phpass
Modernized, namespaced fork of Openwall Phpass (0.3) with Composer autoloading and unit tests. Provides PasswordHash for hashing and verifying passwords with minimal stylistic changes; public domain code, PHP 5 style.
Installation:
composer require bordoni/phpass
Add to composer.json under "require":
"bordoni/phpass": "^0.3.6"
First Use Case: Hash and verify a password in a Laravel controller:
use Hautelook\Phpass\PasswordHash;
// Initialize with cost factor (8-31) and portability mode
$hasher = new PasswordHash(8, false);
// Hash a password (e.g., during registration)
$hash = $hasher->HashPassword('user_provided_password');
// Verify a password (e.g., during login)
$isValid = $hasher->CheckPassword('user_input_password', $stored_hash_from_db);
Where to Look First:
src/PasswordHash.php for method signatures, edge cases, and internal logic.Registration Flow:
// In a Laravel controller or service
$hasher = resolve(PasswordHash::class); // If bound in service provider
$hash = $hasher->HashPassword(request('password'));
User::create([
'email' => request('email'),
'password' => $hash,
]);
Login Flow:
$user = User::where('email', request('email'))->first();
if ($user && $hasher->CheckPassword(request('password'), $user->password)) {
// Authenticate user
}
Password Reset:
// Generate a token, store it, then reset:
$hasher->HashPassword(request('new_password')) // New hash for DB
Service Provider Binding:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->bind(PasswordHash::class, function ($app) {
return new PasswordHash(
config('auth.password.cost', 8),
config('auth.password.portable', false)
);
});
}
Configure in config/auth.php:
'password' => [
'cost' => 10, // Default cost factor
'portable' => false, // Enable only for legacy systems
],
Form Request Validation:
use Illuminate\Validation\Rule;
public function rules()
{
return [
'password' => [
'required',
'string',
'min:8',
Rule::unique('users')->ignore($this->user),
],
];
}
Testing:
// Mock PasswordHash in unit tests
$mock = Mockery::mock(PasswordHash::class);
$mock->shouldReceive('HashPassword')
->with('test_password')
->andReturn('$2a$08$hashed_value');
$this->app->instance(PasswordHash::class, $mock);
Schema::table('users', function (Blueprint $table) {
$table->string('password')->nullable()->change();
});
$users = User::all();
$newHasher = new PasswordHash(10, false);
foreach ($users as $user) {
$oldHasher = new PasswordHash(4, true); // Match old cost
$hash = $newHasher->HashPassword($user->password);
$user->update(['password' => $hash]);
}
Cost Factor Misconfiguration:
<8) weakens security; too high (e.g., >12) causes performance bottlenecks.8 or 10 for most applications. Benchmark under load.Portability Mode:
portable_hashes=true reduces security by supporting older hash formats.PHP Version Incompatibilities:
0.3.6).^0.3.6:
composer require bordoni/phpass:^0.3.6
Hash Format Assumptions:
$2a$. Mixing with Laravel’s native bcrypt (e.g., $2y$) will fail.Database Truncation:
varchar(60) column may truncate long hashes (unlikely but possible).varchar(255) for safety.Failed Verification:
CheckPassword returns false for correct credentials.\n, \r) in the input password.\Log::debug('Stored hash:', [$user->password]);
\Log::debug('Input password:', [request('password')]);
Performance Issues:
8 to 10) and test.PasswordHash instance (it’s stateless but instantiation is cheap).Deprecation Warnings:
intval() expects parameter 1 to be int.0.3.6 or patch locally:
// In PasswordHash.php, replace:
intval($this->iterations)
// With:
(int)$this->iterations
Dynamic Cost Factor:
// Adjust cost based on server load or user role
$cost = config('hash.cost');
if (app()->environment('production')) {
$cost = min(12, $cost); // Cap at 12 for production
}
$hasher = new PasswordHash($cost, false);
Password Strength Enforcement: Combine with Laravel’s validation:
use Illuminate\Validation\Rules\Password as PasswordRule;
'password' => [
'required',
'string',
new PasswordRule, // Built-in strength rules
'min:8',
],
Audit Logging:
// Log failed attempts
if (!$hasher->CheckPassword($input, $stored)) {
event(new FailedLoginAttempt(
user: $user,
ip: request()->ip(),
timestamp: now()
));
}
Legacy Hash Migration:
// Rehash passwords from an old system
$legacyHasher = new PasswordHash(4, true); // Match old cost
$newHasher = new PasswordHash(10, false);
User::chunk(100, function ($users) use ($legacyHasher, $newHasher) {
foreach ($users as $user) {
// Verify old hash first (optional)
if ($legacyHasher->CheckPassword('old_password', $user->password)) {
$user->password = $newHasher->HashPassword('old_password');
$user->save();
}
}
});
Custom Hash Storage:
// Store additional metadata with the hash
$hashData = [
'hash' => $hasher->HashPassword($password),
'cost' => 8,
'created_at' => now(),
];
$user->password = json_encode($hashData);
Cost Factor Limits:
4–31. Values outside this range will be clamped.Portability Mode:
true, Phpass supports older hash formats (e.g., $2y$ from other bcrypt implementations). This may introduce compatibility issues.**Thread
How can I help you explore Laravel packages today?