不是。Laravel密码重置Token使用bin2hex(random_bytes(32))生成64字符十六进制字符串,经hash_hmac('sha256', $token, app.key)哈希后存储,验证时重新计算比对,确保安全性、一次性及防重放。

Token 是用 Str::random(32) 生成的吗?
不是。Laravel 的密码重置 Token 并非简单调用 Str::random(32),而是通过 Illuminate\Auth\Passwords\TokenRepository 使用 bin2hex(random_bytes(32)) 生成 64 位十六进制字符串(即 32 字节随机字节转为 64 字符)。这个过程依赖 PHP 的 random_bytes(),满足密码学安全要求,避免被预测或暴力枚举。
关键点:
-
random_bytes()在 PHP 7+ 中由操作系统 CSPRNG(如/dev/urandom或CryptGenRandom)提供,不可被用户空间伪随机数替代 - Token 存入数据库前会用
hash_hmac('sha256', $token, config('app.key'))哈希存储,原始 token 永远不落库 - 所以数据库里存的是
hash_hmac结果,不是明文 token,即使库泄露也无法直接用于重置
验证时为什么不用查原始 token?
因为数据库存的是哈希值,验证逻辑是:收到请求中的 $token 参数后,用同样密钥和算法重新计算 hash_hmac('sha256', $token, config('app.key')),再与数据库中记录的哈希比对。这属于「哈希比对」而非「明文匹配」。
这样做可规避以下风险:
- 防止数据库被拖库后攻击者直接拿 token 重放请求
- 避免在日志或审计中意外暴露原始 token(因为业务代码永远只处理输入的 token,不读取存储的哈希值作其他用途)
- 支持 token 一次性使用:验证成功后,
TokenRepository::delete()立即删掉该哈希记录,后续相同 token 请求必然失败
Token 过期时间由谁控制?
过期逻辑完全在应用层控制,不依赖数据库字段或缓存 TTL。核心判断在 Illuminate\Auth\Passwords\PasswordBroker::validateNewPassword() 和 TokenRepository::exists() 中:
public function exists($user, $token)
{
$record = $this->getTable()->where('email', $user->getEmailForPasswordReset())
->where('token', hash_hmac('sha256', $token, config('app.key')))
->first();
return $record && $record->created_at->gt(now()->subMinutes($this->expires));
}
注意:$this->expires 默认是 60 分钟,来自 config/auth.php 中的 'expire' => 60,单位为分钟;created_at 是 token 创建时写入的时间戳,验证时仅做时间比较,不更新、不延长。
这意味着:
- Token 生命周期严格固定,无法通过刷新页面或重复请求续期
- 时间判断基于服务器本地时间(
now()),需确保服务器时间同步(NTP),否则可能提前失效或延迟拒绝 - 没有后台定时任务清理过期记录——清理靠用户触发验证时的条件查询 + 成功后主动删除,因此表中可能残留少量过期未使用的记录
为什么不能自己用 md5(time().rand()) 替代?
这种写法在 Laravel 密码重置流程中会直接破坏安全性模型:
-
md5不抗碰撞,且无密钥,攻击者可轻易伪造哈希值 -
time()和rand()都是非加密级随机源,可被预测(尤其在容器或低熵环境中) - 没做一次性校验,没绑定用户邮箱,没防重放,也没时间窗口控制
- Laravel 的整个
PasswordBroker流程(包括邮件生成、路由签名、中间件拦截)都假设 token 来自TokenRepository的标准实现,自定义 token 会导致reset路由 404 或 403
真正需要定制时,应继承 TokenRepository 并重写 create() 和 exists(),但必须保持 HMAC + 时间判断 + 一次性语义不变——否则就不是“增强”,而是“挖坑”。










