[红日安全]代码审计Day4 - strpos使用不当引发漏洞
字数 1261 2025-08-29 08:32:24
PHP代码审计:strpos使用不当引发的安全漏洞分析
1. 漏洞背景
本文分析PHP代码审计中因strpos()函数使用不当导致的安全漏洞,并结合实际案例(DeDecms V5.7SP2)展示类似逻辑缺陷引发的安全问题。
2. 示例漏洞分析
2.1 原始漏洞代码
// 示例代码片段
class Login {
public function __construct($user, $pass) {
$this->loginViaXml($user, $pass);
}
public function loginViaXml($user, $pass) {
if (strpos($user, '<') !== false || strpos($pass, '<') !== false) {
die('非法字符');
}
if (strpos($user, '>') !== false || strpos($pass, '>') !== false) {
die('非法字符');
}
$format = '<user><username>%s</username><password>%s</password></user>';
$xml = sprintf($format, $user, $pass);
// 使用XML进行登录验证...
}
}
new Login($_POST['user'], $_POST['pass']);
2.2 漏洞原理
-
strpos函数特性:
- 返回匹配子字符串的位置(0表示开头匹配)
- 未找到时返回false
false == 0在PHP中为true
-
错误防护逻辑:
if (strpos($user, '<') !== false) // 正确写法 if (!strpos($user, '<')) // 错误写法(漏洞点) -
漏洞利用:
- 当输入以
<开头时,strpos()返回0 !0在PHP中等于true,导致防护被绕过- 可构造XML注入payload:
user=<"><injected-tag%20property="&pass=<injected-tag>
- 当输入以
3. 实际案例分析:DeDecms V5.7SP2密码重置漏洞
3.1 漏洞位置
member/resetpassword.php文件中的安全问答验证逻辑
3.2 漏洞代码
// 安全问答验证
if($dopost == "safequestion") {
$row = $db->GetOne("SELECT safequestion,safeanswer,userid,email FROM #@__member WHERE mid = '$mid'");
if(empty($safequestion)) $safequestion = '';
if(empty($safeanswer)) $safeanswer = '';
// 漏洞点:使用弱类型比较
if($row['safequestion'] == $safequestion && $row['safeanswer'] == $safeanswer) {
sn($mid, $row['userid'], $row['email'], 'N');
exit();
}
}
3.3 漏洞原理
-
弱类型比较问题:
- 未设置安全问答时,数据库值为
$row['safequestion']="0",$row['safeanswer']=null - 使用
==比较时:"0" == ""→ falsenull == ""→ true
- 可绕过payload:
0.0、0.、0e1
- 未设置安全问答时,数据库值为
-
利用链:
1. 访问resetpassword.php?dopost=safequestion&safequestion=0.0&safeanswer=&id=[目标ID] 2. 获取返回的key值 3. 访问修改密码链接:resetpassword.php?dopost=getpasswd&id=[目标ID]&key=[获取的key]
3.4 漏洞修复
将弱类型比较==改为严格比较===:
if($row['safequestion'] === $safequestion && $row['safeanswer'] === $safeanswer)
4. 防御建议
-
strpos正确用法:
// 正确方式 if (strpos($input, '<') !== false) { // 包含<字符 } // 错误方式(易被绕过) if (!strpos($input, '<')) { // 误判0为false } -
类型安全比较:
- 始终使用
===和!==进行严格比较 - 特别注意:
0、"0"、""、null、false的区别
- 始终使用
-
输入过滤:
- 对XML/HTML/SQL等特殊上下文使用专用过滤函数
- 如
htmlspecialchars()、mysqli_real_escape_string()等
-
安全编码实践:
- 使用白名单而非黑名单验证
- 对边界条件进行全面测试(如空值、0值、特殊字符等)
5. 总结
- PHP的弱类型系统和特定函数行为可能引入安全隐患
strpos()等函数需注意返回值0与false的区别- 比较操作应优先使用严格模式(
===/!==) - 安全验证逻辑需要覆盖所有可能的输入情况
- 实际漏洞常由多个小问题组合触发,需全面审计
6. 扩展学习
-
PHP类型比较表:
值1 值2 == === 0 "" true false 0 "0" true false null "" true false false "" true false -
相关函数注意事项:
stripos():不区分大小写版本,有相同问题strstr()/stristr():返回子字符串或falsepreg_match():返回匹配次数(0或1),需用===1判断
-
推荐审计工具:
- PHPStan
- Psalm
- RIPS静态分析工具