弱类型比较
== 与 ===
在PHP中比较符号有 == 与 === 两种,== 为松散比较,另一种 === 则为严格比较,我们通过官方手册的表格可以了解到类型转换以及比较, PHP手册 == 与 === 表格


当使用松散比较 == 时,PHP会首先将类型转化为统一类型后再进行比较,比如一个数字和一个字符串进行比较,PHP会把字符串转换成数字再进行比较。PHP转换的规则的是:若字符串以数字开头,则取开头数字作为转换结果,如下图

值得注意的是,在PHP 8.0 版本中对 == 字符与数字对比做出了优化, 例如:var_dump('a' == 0); 在新版本中则返回False
md5()
md5() 函数常见用于源码中的用户密码输入加密,与数据库中的 md5加密密码对比,由于PHP弱类型的特性,当代码不够严谨时,将导致例如登录等的绕过,我们看下面的一段代码
<?php
if($_GET['a'] != $_GET['b']){
if(md5($_GET['a']) == md5($_GET['b'])){
echo 'Success';
}
}
?>
代码中首先判断 a 与 b 传入的参数不允许相同,但后半段则要求 md5加密值相同,按逻辑看来似乎是不可能的。但由于PHP中,0e开头的字符串会被判定为科学计数法,而0的 x次方均为0,所以绕过就可以通过传参开头均为 0e 的字符赋值给 a 与 b 来进行绕过

| 字符串 | md5值 |
|---|---|
| QNKCDZO | 0e830400451993494058024219903391 |
| 240610708 | 0e462097431906509019562988736854 |
| s878926199a | 0e545993274517709034328855841020 |
| s155964671a | 0e342768416822451524974117254469 |
| s214587387a | 0e848240448830537924465865611904 |
| s214587387a | 0e848240448830537924465865611904 |
| s878926199a | 0e545993274517709034328855841020 |
| s878926199a | 0e545993274517709034328855841020 |
| s1665632922a | 0e731198061491163073197128363787 |
| s1836677006a | 0e481036490867661113260034900752 |
由于弱类型导致了松散比较 == 的绕过,于是大部分源码中对使用的则为 === ,而强比较 在md5()函数中的应用上也存在代码逻辑不严谨导致的漏洞 , 如下面一段代码
<?php
if($_GET['a'] != $_GET['b']){
if(md5($_GET['a']) === md5($_GET['b'])){
echo 'Success';
}
}
?>
这一段代码中将 == 转为 ===,使用刚刚的方法则无法通过if语句,但由于 md5() 函数是无法处理数组的,当变量为数组传入 md5 中时,不会立刻停止运行php程序,而是抛出warning,得到的结果为 False 或 Null
同样,类似的函数例如 sha1(), 同样也会出现相似的问题

strcmp()
strcmp函数用于两个字符串之间的比较,当函数使用不当将会导致绕过

在手册下我们可以看到曾经的相关留言

<?php
if (strcmp($_POST['password'], 'sekret') == 0) {
echo "Welcome, authorized user!\n";
} else {
echo "Go away, imposter.\n";
}
?>
可以看到当 strcmp函数 传入数组时,达到绕过的效果,这是由于 strcmp函数无法处理数组导致的(与md5函数类似,返回Null),当传入数组时无论与任何字符做比较都会得到 Null

在 PHP <= 5.3 版本时,显示了报错的警告信息后,将return 0,在 PHP >= 5.3 版本时,修复为返回为 Null。同样,类似的还有 strpos() 等无法处理数组的函数