当前位置:首页>php>PHP代码审计中:弱类型与函数比较缺陷

PHP代码审计中:弱类型与函数比较缺陷

  • 2026-10-10 13:50:30
PHP代码审计中:弱类型与函数比较缺陷

一、核心基础:== 与 === 的本质区别

这是所有弱类型缺陷的根源,审计时首先要关注所有比较操作符的使用场景:

特性
松散比较(==)
严格比较(===)
比较规则
先隐式类型转换,再比较值
不转换类型,同时比较值 + 类型
安全风险
极高(易被绕过)
极低(安全)
审计重点场景
登录验证、权限校验、参数白名单
无(推荐使用)

典型审计案例:

// 审计发现:登录校验用==,可被绕过if($_POST['password'] == $admin_pwd) { // 攻击者传0/0e123等可绕过    $_SESSION['admin'] = true;}

二、细分缺陷场景(含审计要点 + 案例)

1. MD5 比较缺陷(0e 开头的 “魔术字符串”)

原理:MD5 哈希值以0e开头时,PHP 会将其视为科学计数法(0 的任意次方仍为 0),使用==比较时,所有0exxxx格式的 MD5 值都会被判定为相等。

审计特征:代码中存在 md5($a) == md5($b) 或 md5($input) == $固定值。

漏洞案例:

// 审计发现:MD5比较用==,存在绕过风险$token = $_GET['token'];if(md5($token) == "0e123456789012345678901234567890") {    echo "验证通过";}// 攻击者只需传:token=QNKCDZO(md5(QNKCDZO)=0e830400451993494058024219903391)// 所有0e开头的MD5值都会被==判定为相等

补充:常见的 0e 开头 MD5 “魔术字符串”:

QNKCDZO → 0e830400451993494058024219903391

240610708 → 0e462097431906509019562988736854

s878926199a → 0e545993274517709034328855841020

2. strcmp () 函数类型比较缺陷

原理:strcmp($a, $b) 设计用于比较字符串,但:

若传入非字符串类型(如数组),函数返回null;

null == 0(松散比较),导致校验被绕过。

审计特征:代码中存在 strcmp(用户输入, 固定值) == 0,且未校验输入类型。

漏洞案例:

// 审计发现:strcmp比较无类型校验$pwd = $_GET['pwd'];if(strcmp($pwd, $correct_pwd) == 0) { // 攻击者传?pwd[]=任意值    echo "登录成功"; // strcmp返回null,null==0为true}

3. Bool 类型比较缺陷

原理:PHP 中非布尔值会隐式转为布尔值:

空字符串""、0、null、空数组[]、false → 转为false;

非空字符串、非 0 数字、非空数组 → 转为true。松散比较时易被绕过。

审计特征:用==比较布尔值(如 $input == true),或直接将用户输入作为布尔判断条件。

漏洞案例:

// 审计发现:布尔比较逻辑缺陷$is_admin = $_GET['admin'];if($is_admin == true) { // 攻击者传?admin=123(非0数字→true)或?admin=任意字符串    echo "管理员权限";}// 另一个场景:空数组绕过$check = $_GET['check'];if($check == false) { // 传?check[]=1 → 空数组?不,非空数组→true;传?check=0 或 ?check= → false    echo "校验通过";}

4. switch 类型比较缺陷

原理:switch 内部使用松散比较(==) 匹配case,而非严格比较,会触发隐式类型转换。

审计特征:switch(用户输入) 搭配case 数字/字符串,无类型校验。

漏洞案例:

php

// 审计发现:switch松散比较绕过$action = $_GET['action'];switch($action) {    case 1: // 攻击者传?action=1abc → 转为1,匹配成功        echo "执行管理员操作";        break;    case 2:        echo "执行普通操作";        break;}

5. in_array 数组比较缺陷

原理:in_array($needle, $haystack, $strict) 第三个参数默认false(松散比较),用户输入会被隐式转换后匹配数组值。

审计特征:in_array(用户输入, 白名单数组) 未开启严格模式。

漏洞案例:

// 审计发现:in_array无严格模式$allowed_roles = [1, 2, 3]; // 1=管理员,2=普通用户,3=访客$user_role = $_GET['role'];if(in_array($user_role, $allowed_roles)) { // 传?role=1abc → 转为1,匹配成功    echo "角色合法,执行操作";}

6. === 数组比较缺陷(MD5 返回 NULL)

原理:

md5() 仅接受字符串 / 数字类型,若传入数组,会返回null;

用===比较两个null(如md5($arr1) === md5($arr2))会返回true,导致校验失效。

审计特征:md5(用户输入) === 固定MD5值,但未校验输入是否为数组。

漏洞案例:

// 审计发现:MD5+===但未校验输入类型$token = $_GET['token'];$correct_md5 = "e10adc3949ba59abbe56e057f20f883e"; // md5(123456)if (md5($token) === $correct_md5) { // 传?token[]=1 → md5($token)=null,与$correct_md5不相等?    // 修正案例:攻击者传两个数组,md5($arr1)=null,md5($arr2)=null,===会相等    $a = $_GET['a'];    $b = $_GET['b'];    if (md5($a) === md5($b)) { // 传?a[]=1&b[]=2 → 两个md5都返回null,===为true        echo "校验通过";    }}

三、补充其他高频比较缺陷(审计易遗漏)

1. array_key_exists () 隐式转换缺陷

原理:array_key_exists($key, $arr) 对数字字符串键名会隐式转换(如键名123和字符串"123"视为同一个键)。

审计特征:array_key_exists(用户输入, 权限数组) 用于权限控制。

案例:

$admin_keys = [100 => 'admin', 200 => 'user'];$key = $_GET['key'];if(array_key_exists($key, $admin_keys)) { // 传?key=100abc → 转为100,匹配成功    echo "有权限";}

2. intval () 函数的截断缺陷

原理:intval($str) 会截取字符串开头的数字部分(如intval("100abc")=100),松散比较时易被利用。

审计特征:intval(用户输入) == 固定值 用于 ID / 权限校验。

案例:

$admin_id = 100;$user_id = intval($_GET['id']);if ($user_id == $admin_id) { // 传?id=100abc → intval=100,匹配成功    echo "管理员权限";}

3. strpos () 函数的 0 值混淆缺陷

原理:strpos($str, $sub) 查找子串位置,返回:

数字(位置)或false(未找到);

0(子串在开头)与false松散比较(0 == false)会被误判为未找到。

审计特征:strpos(用户输入, 关键字) == false 用于内容校验。

案例:

// 审计发现:strpos比较用==,导致开头匹配被误判$content = $_GET['content'];if(strpos($content, '危险关键词') == false) { // 传content=危险关键词123 → strpos返回0,0==false为true    echo "内容合法"; // 实际包含危险关键词,却被放行}// 正确写法:strpos(...) === false

4. empty () 函数的类型缺陷

原理:empty($var) 会判断多种 “空值”,包括:""、0、0.0、"0"、null、[]、false,松散的判断逻辑易被绕过。

审计特征:empty(用户输入) 用于必填参数校验。

案例:

// 审计发现:empty校验手机号,0开头号码被误判$phone = $_GET['phone'];if (empty($phone)) { // 传phone=0123456789 → "0123456789"非空,但传phone=0 → empty(0)=true    echo "手机号不能为空";}// 正确写法:!isset($phone) || $phone === ""

四、审计修复通用方案

针对所有弱类型 / 比较缺陷,审计时需督促开发遵循以下规则:

强制严格比较:安全场景(登录、权限、校验)必须用===/!==,杜绝==/!=;

前置类型校验:对用户输入先校验类型(is_string()/is_int()/is_array()),再处理;

显式类型转换:关键参数手动转换为目标类型(如$id = (int)$_GET['id']);

开启函数严格模式:in_array()/array_search() 必须加第三个参数true;

避免直接用用户输入作为判断条件:布尔判断前先做值 + 类型校验;

特殊函数加固:

strcmp():先校验is_string(),再用=== 0比较;

strpos():用=== false判断是否未找到;

md5():先校验输入为字符串,再比较。

最新文章

随机文章