PHP 8.1.16+如何兼容旧版crypt()含非法盐的哈希行为?
问题解答
能否在新版PHP中复刻旧版crypt()行为?
很难直接在新版PHP中原封不动复刻该行为——PHP 8.1.16的这个变更属于底层加密库的安全合规性修复,官方没有提供开关回退到旧逻辑。但可以通过两种方式模拟旧版行为:
- 手动修复盐值后调用新版crypt():
旧版crypt()对不符合Blowfish规范的盐会自动将非法字符(如$)替换为.,并调整盐长度到标准的22位。你可以手动对原始预存盐做相同处理,生成合规的Blowfish盐后调用新版crypt()。
以你的示例为例,原始盐$2a$05$SomeBadSaltHasDollar/$中的末尾$替换为.后,得到合规盐$2a$05$SomeBadSaltHasDollar.,用这个盐调用crypt()即可生成与旧版一致的哈希。 - 搭建旧版PHP验证服务:
部署一个PHP 8.1.15及以下版本的轻量服务,提供简单接口接收用户凭证和原始盐,返回旧版crypt()生成的哈希。新版PHP通过调用该接口获取验证所需的哈希值,这是最稳妥的模拟方式。
无法复刻时,如何利用现有旧数据?
如果上述模拟方式不可行,可通过以下方案处理:
- 用户登录时迁移数据:
- 用户输入凭证后,先通过旧版逻辑(如调用旧版PHP服务)验证旧哈希是否匹配。
- 验证成功后,立即用合规的哈希方案(如
password_hash())重新生成凭证的哈希,替换数据库中的旧哈希。后续验证直接使用新版PHP的标准哈希逻辑。
- 尝试用旧哈希直接验证:
新版crypt()支持用完整哈希作为盐值,自动提取其中的有效盐部分进行验证,你可以尝试这个逻辑:
注意:如果旧哈希的盐部分仍包含非法字符,新版crypt()仍会返回$userInput = 'my password'; $oldHash = '$2a$05$SomeBadSaltHasDollar/.ry.LY8GXnJWLU9/BIJ5I4VJPRBlp6z.'; $newHash = crypt($userInput, $oldHash); if ($newHash === $oldHash) { // 验证成功,可查询对应ImportantValue }*0,此时仍需结合旧版服务完成验证。
内容的提问来源于stack exchange,提问作者Michael David
相关产品推荐
相关产品推荐

