
PHP代码审计实战从array_merge到extract的变量覆盖漏洞链深度解析1. 漏洞链的起点array_merge函数特性分析在PHP开发中array_merge()函数常被用于合并数组但其某些特性往往被开发者忽视。让我们先看一个典型的安全误用案例private $date [version1.0, imghttps://example.com/image.jpg]; public function __construct($data) { $this-date array_merge($this-date, $data); }关键风险点当合并的数组包含相同字符串键名时后者会覆盖前者未对输入参数$data进行类型检查和过滤合并后的数组直接赋给类属性为后续漏洞埋下伏笔下表展示了array_merge在不同场景下的行为差异场景输入1输入2输出结果安全风险正常合并[a1][b2][a1,b2]无键名覆盖[a1][a2][a2]中数字键处理[0a][0b][0a,1b]低实际开发中建议使用array_replace_recursive()替代array_merge()进行可控的数组合并或使用以下安全封装function safe_merge($default, $input) { return is_array($input) ? array_merge($default, $input) : $default; }2. 漏洞链延伸extract函数的危险用法当array_merge生成的数组遇到extract函数时风险被进一步放大。观察以下典型漏洞代码public function display($template, $space) { extract($this-date); // 高危操作 $this-getTempName($template, $space); include($this-template); }extract的安全隐患变量注入将数组键名直接转为变量名作用域污染可能覆盖已有变量结合include可能导致文件包含漏洞推荐的安全实践// 安全做法1明确指定extract规则 extract($this-date, EXTR_SKIP); // 不覆盖已有变量 // 安全做法2使用提取函数 function safe_extract($array, $prefix) { foreach($array as $k $v) { $varName $prefix . $k; if(!isset($$varName)) { $$varName htmlspecialchars($v, ENT_QUOTES); } } }3. 完整攻击链构建结合上述两个漏洞点攻击者可以构造如下攻击路径初始控制点通过POST参数控制$data内容数组覆盖注入特定键值覆盖$this-date变量注入通过extract将恶意参数转为变量最终利用控制include路径实现RCE典型攻击PayloadPOST /vulnerable.php HTTP/1.1 Content-Type: application/x-www-form-urlencoded templatemaliciousspaceadminmodactionfunction namesystem paramid4. 防御方案与最佳实践4.1 输入过滤层class SecurityFilter { public static function cleanArray($input) { if(!is_array($input)) return []; $clean []; foreach($input as $k $v) { $k preg_replace(/[^a-z0-9_]/i, , $k); $v is_array($v) ? self::cleanArray($v) : htmlentities($v, ENT_QUOTES); $clean[$k] $v; } return $clean; } } // 使用示例 $cleanData SecurityFilter::cleanArray($_POST);4.2 安全编码方案方案1白名单控制private $allowedKeys [template, space, mod]; public function __construct($data) { $filtered array_intersect_key($data, array_flip($this-allowedKeys)); $this-date array_merge($this-date, $filtered); }方案2类型约束class StrictData { public string $template index.html; public string $space ; public string $mod ; public function __construct(array $data) { foreach($data as $k $v) { if(property_exists($this, $k) is_string($v)) { $this-$k $v; } } } }4.3 架构级防护对于关键系统建议采用以下架构设计使用DTO(Data Transfer Object)模式处理输入实现自动化的参数绑定机制在框架层禁用危险函数// 在php.ini中禁用危险函数 disable_functions extract,parse_str,assert,system,passthru5. 漏洞检测与自动化审计5.1 静态检测规则使用正则表达式检测危险模式/(array_merge\s*\(.*?\$_(GET|POST|REQUEST)|extract\s*\(.*?\$[a-zA-Z_])/5.2 动态测试方案构建自动化测试脚本import requests test_cases [ {input: {template: test}, expected: 200}, {input: {template: ../../etc/passwd}, expected: 403}, {input: {space: admin}, expected: 200} ] for case in test_cases: r requests.post(TARGET_URL, datacase[input]) assert r.status_code case[expected], fFailed test: {case}6. 真实案例复盘某CMS系统曾因类似漏洞链导致大规模入侵攻击流程如下攻击者发现未过滤的array_merge调用通过精心构造的POST数据覆盖配置参数利用extract注入数据库连接参数最终获取数据库管理员权限修复方案对比修复方式实施难度防护效果性能影响输入过滤低中低参数白名单中高低架构重构高极高中在最近参与的某金融项目代码审计中我们发现并修复了3处类似的变量覆盖漏洞。通过hook关键函数进行监控有效拦截了多次攻击尝试。