PHP 弱类型陷阱:那些等号比较坑过你的地方
PHP 的 == 会先做类型转换再比较,=== 不会——所有涉及身份、权限、金额、Token 的比较,一律用 === 或 hash_equals(),这一条能挡掉九成以上的弱类型事故。
一、坑的根源:== 的类型转换规则
结论:== 在不同 PHP 版本上的行为并不一致,这才是最危险的地方。
PHP 8.0 之前,非数字字符串和数字比较会先把字符串转成数字,"abc" == 0 返回 true;PHP 8.0 起改为按字符串比较,返回 false。同一行代码,PHP 7.4 和 8.0 结果相反。Clara BBS 的兼容区间是 PHP 7.4-8.5,同一份代码可能跑在完全不同的比较语义上——如果你写插件时习惯用 == 判断版本号、状态码、用户输入,升级 PHP 时行为会静默变化,不报错,但逻辑已经错了。
另一个经典是「0e 魔法哈希」:PHP 里 "0e123" 会被当成科学计数法(0 乘以 10 的 123 次方,结果都是 0),所以两个都以 0e 开头的 MD5 值用 == 比较会相等。凡是拿哈希当密码或签名比较的地方,必须用 hash_equals($known, $user),它同时防类型绕过和时序攻击。
还有「五兄弟」问题:null、false、0、""、"0" 之间用 == 互比,结果经常不符合直觉。null == 0 为 true,"" == 0 为 true。
二、这些函数内部也在用 ==
结论:in_array()、array_search()、switch、strpos() 都默认走宽松比较,必须主动收紧。
in_array($needle, $haystack)默认松散比较,第三个参数传true才是严格模式。结论:in_array()永远写满三个参数:in_array($needle, $haystack, true)。array_search()同理,第三参数strict要传true。switch用的是==语义,switch("abc")在 PHP 7 下能命中case 0,PHP 8 下不会。strpos()找不到时返回false,找到开头时返回0,而0 == false为true。结论:判断用if (strpos($s, $needle) === false),或直接用str_contains()(PHP 8.0+)。
另外澄清一个高频误解:declare(strict_types=1) 只影响函数参数和返回值的类型强制,不影响 ==、in_array() 这类比较操作。开了严格模式不等于比较就安全了。
三、放到论坛系统里,这些坑长什么样
结论:身份、权限、金额三类比较,是最容易被弱类型钻空子的地方。
Clara BBS 在安全基线上把这几件事交给了更稳妥的机制:密码用 bcrypt 存储,校验走 password_verify(),不是把哈希拿出来用 == 比;CSRF 是全站统一校验;上传走文件类型白名单加图片二次校验。这些设计本身就是绕开弱类型比较的思路。
但二次开发和写插件时,坑得自己躲:
- 权限判断。用户组、版块权限这类值常见写法是
if ($groupId == 1),如果$groupId来自请求参数、是字符串"1abc",PHP 7 下会命中管理员分支。改用===并先做intval()归一,或直接把允许的组放进数组用in_array(..., true)判断——Clara BBS 的版块级权限矩阵本身就是「用户组 × 操作」的枚举匹配,插件里沿用这套枚举比自造数值判断安全得多。 - 金额与余额。悬赏托管、附件定价、转账记账都绕不开数值比较。浮点数用
==几乎必错(0.1 + 0.2 == 0.3是false),涉及金额时统一用整数最小单位,或至少用abs($a - $b) < 0.0001这种容差写法。 - Token 与签名。任何和用户输入的密钥、令牌比较,一律
hash_equals()。 - 状态码。数据库里的
tinyint状态字段,从 PDO 取出来可能是字符串,别用==和整数常量比,用(int)转换后===。
四、动手清单
结论:把下面四件事做完,弱类型事故基本清零。
- 全项目搜索
==,逐个人工确认,安全相关的一律换成===。 - 搜索
in_array(和array_search(,补上true。 - 搜索
strpos(,确认判断条件写的是=== false。 - 在代码规范里写明:不许用
==比较来自请求、数据库、配置的值——这些来源的类型永远不可信。
判断优先级很简单:能 === 就 ===,哈希用 hash_equals(),数组查找加 strict,金额别用浮点相等。
弱类型不是 PHP 的缺陷,是它的设计取舍;问题在于你没法控制变量从哪来。凡是外部输入参与的比较,都当成「类型未知」处理,用严格比较收口,就不会在升级 PHP 或换环境时被静默改掉逻辑。
转载请注明出处,版权归原作者所有。
正式会员
认证极客






