不能用 Crypt::encrypt() 存密码,因为它是可逆加密,密钥泄露会导致所有密码被批量解密;而 Hash::make() 使用 bcrypt 哈希,不可逆、自带盐、抗暴力破解且不依赖 APP_KEY。

为什么不能用 Crypt::encrypt() 存密码
因为它是可逆的,而密码必须不可逆。一旦攻击者拿到数据库和应用密钥(比如 APP_KEY 泄露),就能批量解密所有密码——这违背了密码存储的基本安全原则。Laravel 的 Crypt 门面设计目标是保护「需要还原的数据」,比如 API 密钥、用户偏好设置;它底层用 AES-256-CBC,依赖 APP_KEY 加解密,密钥一旦失守,全盘失效。
常见错误现象:
- 开发者把
Hash::make()误写成Crypt::encrypt(),导致密码能被解出 - 在迁移旧系统时,直接加密明文密码而非重新哈希,验证逻辑崩溃
Hash::make() 底层调用的是 PHP 原生 password_hash()
Laravel 没有自己实现哈希算法,而是封装了 PHP 7.2+ 的 password_hash() 函数,默认使用 bcrypt(PASSWORD_BCRYPT)。这意味着:
- 每次调用
Hash::make('123')都会生成新盐(salt),即使密码相同,哈希值也不同 - 哈希字符串形如
$2y$10$...,其中10是 cost factor,代表计算强度,可防暴力破解 - 它自动把 salt 和哈希结果打包进一个字符串,无需单独存 salt 字段
你不需要、也不应该手动拼接 salt 或调用 crypt() 函数——Hash::make() 已经做了全部正确的事。
Hash::check() 验证时不会暴露原始密码或哈希结构
这个函数不是简单字符串比对,而是调用 PHP 的 password_verify(),它内部会解析哈希字符串中的算法标识、cost 和 salt,再用完全相同的参数重算一次哈希,然后恒定时间比对(timing-safe compare)。这能防止时序攻击。
容易踩的坑:
- 自己用
==或===直接比对哈希字符串(不安全,且可能因格式差异失败) - 在验证前对输入密码 trim() 或 strtolower() ——
Hash::check()不做任何预处理,空格和大小写都影响结果 - 把
Hash::check()放在 try/catch 里捕获异常——它只返回布尔值,不会抛异常
加密密钥(APP_KEY)泄露会导致 Crypt 全线崩塌
Crypt 安全的前提是 APP_KEY 保密且唯一。这个 32 字符 base64 字符串用于 AES 加密的主密钥,一旦硬编码在 Git、日志或环境变量中被公开,所有用 Crypt::encrypt() 加密的数据都可被还原。
真实场景中常见风险点:
- 本地开发用
php artisan key:generate后直接 commit 到仓库 - 在 CI/CD 流程中把
APP_KEY打印到控制台日志 - 多个环境共用同一个
APP_KEY,测试库被拖库后连生产加密数据也遭殃
哈希则完全不依赖 APP_KEY:哪怕整个 .env 文件泄露,bcrypt 哈希依然安全——因为它不靠密钥,靠的是算法本身的计算成本和随机盐。











