密码哈希必须用 argon2i 或 auto,禁用 md5/sha1;symfony 推荐配置 algorithm: 'argon2i' 或 'auto' 并启用 migrate_from 实现平滑升级。

密码哈希必须用 argon2i 或 auto,别碰 md5 和 sha1
Symfony 默认支持现代哈希算法,但如果你手动调用 password_hash() 或硬编码算法,很容易误用弱算法。直接在配置里声明比在代码里手写更安全。
-
argon2i是当前推荐的首选:抗 GPU 暴力破解,可调内存和时间成本 -
auto更省心——它会自动选系统可用的最强算法(优先argon2id,退化到bcrypt),升级 PHP 后无需改配置 - 绝对避免在任何地方显式使用
md5、sha1、crypt()等裸函数;它们不带盐、不可调参、已被证明不安全 - 检查你的
security.yaml是否包含类似以下配置:
password_hashers: App\Entity\User: 'auto' # 或更明确地: # App\Entity\AdminUser: # algorithm: 'argon2i' # memory_cost: 65536 # time_cost: 4 # threads: 2
MigratingPasswordHasher 不是可选项,而是上线前必配项
你不可能一次性把所有用户密码从 bcrypt 切到 argon2i。用户不会配合“请重新设置密码”。真正的迁移发生在登录验证那一刻——旧哈希通过,新哈希立刻生成并存回数据库。
- 配置里加
migrate_from就启用该机制,例如:migrate_from: ['bcrypt', 'sodium'] - 确保你的 User 实体实现了
PasswordUpgraderInterface,并在upgradePassword()方法里调用$user->setPassword($newHash)+$entityManager->flush() - 漏掉
flush()是最常见错误:密码验证成功了,但旧哈希一直没更新,下次登录还得走一遍慢路径 - 测试时用一个已知
bcrypt哈希的测试账号登录,然后查数据库确认字段是否变成$argon2i$开头
密码找回流程中,令牌必须一次性且有时效,且不能暴露用户存在性
FOSUserBundle 的重置逻辑本身是可靠的,但默认行为容易踩坑:比如 fos_user_resetting_request 页面对不存在邮箱也返回 200,等于帮攻击者做邮箱枚举。
- 重置请求页应统一返回“如果邮箱存在,我们会发送邮件”,不要区分“邮箱未注册”和“发送成功”
- 生成的重置令牌必须用
random_bytes(32)或bin2hex(random_bytes(32)),别用时间戳或自增 ID - 令牌存储需带过期时间(如
expires_at: '+1 hour'),且验证后立即失效(DELETE或设used = true) - 别把令牌放在 URL 查询参数里传给前端(易被日志/代理记录),用 POST body 或短期有效的路由占位符(如
/reset/{token}) - 重置接口
fos_user_resetting_reset必须校验令牌有效性、未过期、未使用,三者缺一不可
敏感操作必须绑定用户上下文,不能只靠令牌
重置密码不是“有令牌就能干”,还要确认这个令牌确实属于当前请求的用户主体。否则中间人截获邮件链接,就能直接重置任意账户。
- 令牌表必须关联
user_id外键,而不是只存令牌字符串 - 在
ResettingController::resetAction()中,先用令牌查出对应User,再检查该用户是否处于可重置状态(如未锁定、邮箱已验证) - 重置成功后,强制清空该用户所有活跃 session(调用
$session->invalidate()),防止旧 session 继续操作 - 别在重置成功页面显示原始密码或明文提示——这是审计高频扣分项
真正难的不是写对那几行配置,而是让哈希迁移、令牌时效、上下文绑定这三件事在所有环境(开发/测试/生产)里始终一致生效。本地跑通不代表线上安全,尤其当数据库迁移脚本漏掉索引、或缓存层绕过令牌校验时,漏洞就藏在“看起来没问题”的地方。











