什么是哈希
哈希(Hashing)指的是通过一种单向变换将明文数据(如密码)转换为定长字符串的处理方式。
相同输入总能生成相同的哈希,但无法通过哈希还原出原始明文。
Laravel 的 Hash 门面支持 bcrypt 和 Argon2 两种哈希算法,用于安全地保存密码。
算法对比
| 算法 | 特点 | 推荐用途 |
|---|
| bcrypt | 可通过工作因子(rounds)调整计算成本,默认算法 | 常规 Web 应用 |
| argon2i | 可调整内存、时间、线程数,能抵御旁路攻击 | 对安全性要求较高的场景 |
| argon2id | argon2i 与 argon2d 的混合,PHC 官方推荐 | 新项目使用 Argon2 时 |
bcrypt 的「工作因子」用于控制生成哈希所需的时间。哈希越慢,抵御暴力破解的能力就越强。随着硬件性能提升,只要相应地增加工作因子即可保持安全等级。
哈希与校验流程
Laravel 默认使用 bcrypt 驱动。可以通过 HASH_DRIVER 环境变量修改。
# .env
HASH_DRIVER=bcrypt # bcrypt / argon / argon2id
要自定义哈希配置,可以通过 config:publish 发布配置文件。
php artisan config:publish hashing
发布后可在 config/hashing.php 中修改默认的工作因子等。
基本用法
对密码进行哈希
将密码明文传入 Hash::make() 就会返回哈希值。请将该哈希值保存到数据库。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class PasswordController extends Controller
{
public function update(Request $request): RedirectResponse
{
$request->validate([
'password' => ['required', 'min:8', 'confirmed'],
]);
$request->user()->fill([
'password' => Hash::make($request->password),
])->save();
return redirect('/profile');
}
}
调整 bcrypt 的工作因子
通过 rounds 选项可调整生成哈希的计算成本。值越大越安全,但耗时也会增加。
默认值(12)在多数应用场景下都足够。
$hashed = Hash::make('plain-text-password', [
'rounds' => 14,
]);
调整 Argon2 的参数
使用 Argon2 时,可通过 memory、time、threads 选项调整计算成本。
$hashed = Hash::make('plain-text-password', [
'memory' => 65536, // KiB
'time' => 4,
'threads' => 2,
]);
| 选项 | 说明 | 默认值 |
|---|
memory | 使用的内存量(KiB) | 65536 |
time | 迭代次数 | 4 |
threads | 使用的线程数 | 1 |
校验密码
使用 Hash::check() 可以判断明文密码是否与已保存的哈希匹配。
这是登录处理中最典型的用法。
use Illuminate\Support\Facades\Hash;
if (Hash::check($request->password, $user->password)) {
// 密码匹配
} else {
// 密码不匹配
}
如果使用 Auth::attempt(),Laravel 会自动进行这一处理,不需要你直接调用。
Hash::check() 适合手动校验当前密码的场合。
// 在修改密码表单中确认当前密码的例子
if (! Hash::check($request->current_password, $request->user()->password)) {
return back()->withErrors(['current_password' => '当前密码不正确。']);
}
是否需要重新哈希
Hash::needsRehash() 可以判断哈希生成时的工作因子是否与当前配置不同。
在调整了工作因子后,可以用它来更新既有哈希。
use Illuminate\Support\Facades\Hash;
if (Hash::needsRehash($user->password)) {
$user->update([
'password' => Hash::make($plainTextPassword),
]);
}
下面是登录成功时进行重新哈希的例子。
// 在登录处理中执行重新哈希
if (Auth::attempt($credentials)) {
if (Hash::needsRehash(Auth::user()->password)) {
Auth::user()->update([
'password' => Hash::make($credentials['password']),
]);
}
return redirect()->intended('/dashboard');
}
随着硬件性能提升,请定期审查工作因子以保持安全性。利用 needsRehash(),可以在用户登录的自然时机顺便更新密码哈希。
哈希算法校验
默认情况下,Hash::check() 会验证哈希是否由当前配置的算法生成。
如果算法不同则会抛出 RuntimeException。
这可以防止篡改哈希算法所带来的攻击。
在需要同时支持多种算法(例如算法迁移期间)时,可以将 HASH_VERIFY 设置为 false 来禁用该校验。
设置 HASH_VERIFY=false 将禁用算法校验。请只在迁移期间使用,迁移完成后请恢复为 true(默认值)。
| 想做的事 | 方法 |
|---|
| 对密码进行哈希 | Hash::make($password) |
| 校验哈希 | Hash::check($plain, $hash) |
| 判断是否需要重新哈希 | Hash::needsRehash($hash) |
| 修改哈希驱动 | 使用 HASH_DRIVER 环境变量 |
| 关闭算法校验 | 设置 HASH_VERIFY=false |
下一步
认证入门
学习使用 Hash::check() 实现登录处理等,了解认证的整体流程。