php 8.0 和 8.2 中 password_verify() 验证失败主因是哈希值在生成、传输或存储环节被污染或截断,如文件含bom、数据库字段截断、误用trim()处理哈希,或受cve-2023-0567漏洞影响(8.0.x/8.1.x/8.2.x旧版)。

PHP 8.0 和 8.2 中 password_verify() 验证失败,绝大多数情况不是函数本身出错,而是哈希值在生成、传输或存储环节被污染或截断。尤其当“首次验证成功、后续全部失败”时,问题几乎都集中在数据洁净性上。
文件编码混杂导致哈希字符串被静默破坏
PHP 源文件若保存为 UTF-8 with BOM、ANSI(Windows-1252)或混合编码,解析器可能在脚本加载时注入不可见字符(如 U+FEFF BOM、零宽空格、多余换行)。这些字符虽不报错,却会污染 password_hash() 输出的哈希值——比如拼接 SQL 语句前变量已含隐藏字节,或 PDO 绑定参数时带入额外空格。最终存入数据库的哈希比原始值多/少几个字节,password_verify() 在二进制层面比对失败,必然返回 false。
- 用编辑器(如 VS Code、Notepad++)将所有 PHP 文件另存为「UTF-8 无 BOM」格式
- 检查关键逻辑文件(如密码重置、登录验证脚本)是否含隐藏字符:可在开头加
var_dump(bin2hex(substr($hash, 0, 4)));对比数据库中取出的哈希前几字节 - 避免在哈希变量上做任何字符串拼接、
echo、error_log()或 JSON 编码操作
哈希值读取或传递过程出错
从 MySQL 取出哈希后,若未原样传给 password_verify(),也容易失败。常见问题包括:
- 数据库字段类型为
VARCHAR(60)—— 虽够 bcrypt,但若未来切换到 Argon2ID(最长约 96 字符),会被截断;应统一设为VARCHAR(255) - 使用
mysql_fetch_row()时列顺序错乱,或 PDO 默认 fetch mode 返回关联数组却按数字索引取值,导致取到null - 读取后误用
trim()、htmlspecialchars()或urldecode()处理哈希字符串 - 调用
password_verify()前未校验$hash是否为非空字符串:is_string($hash) && strlen($hash) >= 60
PHP 版本漏洞影响(需重点排查 8.0.x / 8.1.x / 8.2.x 旧小版本)
CVE-2023-0567 漏洞存在于:
- PHP 8.0.x
- PHP 8.1.x
- PHP 8.2.x
该漏洞会导致 password_verify() 在特定 BCrypt 哈希(salt 中含 $ 字符)下错误返回 true,甚至任意密码都可通过验证。这不是“验证失败”,而是“不该通过却通过了”。但若你观察到的是“应该通过却不通过”,则此漏洞无关,应优先排除编码和读取问题。
建议直接运行 php -v 确认版本,并升级至对应修复版本以上。
密码输入环节被意外处理
用户密码本身也可能被前端或后端悄悄修改:
- 表单提交时 JavaScript 的
.trim()去掉了首尾空格,而注册时密码含空格(如"pass123 ") - 后端接收时用了
filter_input(INPUT_POST, 'pwd', FILTER_SANITIZE_STRING),删掉了特殊字符 - POST 数据经由某些框架中间件自动转义或编码,导致密码内容改变
可在验证前加日志:记录原始 $_POST['password'] 的 bin2hex() 结果,与注册时同密码的哈希比对是否一致。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











