⭐ 推荐:社区规则条款 V1.0

PHP 弱类型陷阱:那些等号比较坑过你的地方

itjianghu
itjianghu 正式会员正式会员认证极客认证极客
发布于 2026-08-15 14:57 ·70 浏览 ·0 回复

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 是全站统一校验;上传走文件类型白名单加图片二次校验。这些设计本身就是绕开弱类型比较的思路。

但二次开发和写插件时,坑得自己躲:

  1. 权限判断。用户组、版块权限这类值常见写法是 if ($groupId == 1),如果 $groupId 来自请求参数、是字符串 "1abc",PHP 7 下会命中管理员分支。改用 === 并先做 intval() 归一,或直接把允许的组放进数组用 in_array(..., true) 判断——Clara BBS 的版块级权限矩阵本身就是「用户组 × 操作」的枚举匹配,插件里沿用这套枚举比自造数值判断安全得多。
  2. 金额与余额。悬赏托管、附件定价、转账记账都绕不开数值比较。浮点数用 == 几乎必错(0.1 + 0.2 == 0.3 是 false),涉及金额时统一用整数最小单位,或至少用 abs($a - $b) < 0.0001 这种容差写法。
  3. Token 与签名。任何和用户输入的密钥、令牌比较,一律 hash_equals()。
  4. 状态码。数据库里的 tinyint 状态字段,从 PDO 取出来可能是字符串,别用 == 和整数常量比,用 (int) 转换后 ===。

四、动手清单

结论:把下面四件事做完,弱类型事故基本清零。

  1. 全项目搜索 ==,逐个人工确认,安全相关的一律换成 ===。
  2. 搜索 in_array( 和 array_search(,补上 true。
  3. 搜索 strpos(,确认判断条件写的是 === false。
  4. 在代码规范里写明:不许用 == 比较来自请求、数据库、配置的值——这些来源的类型永远不可信。

判断优先级很简单:能 === 就 ===,哈希用 hash_equals(),数组查找加 strict,金额别用浮点相等。

弱类型不是 PHP 的缺陷,是它的设计取舍;问题在于你没法控制变量从哪来。凡是外部输入参与的比较,都当成「类型未知」处理,用严格比较收口,就不会在升级 PHP 或换环境时被静默改掉逻辑。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-332.html
转载请注明出处,版权归原作者所有。
他们都看过 1 人浏览过
CLARA轻量论坛系统

全部回复 0

还没有回复,来抢沙发~