PHP trick(代码审计关注点)
字数 1268 2025-08-29 08:32:02

PHP代码审计关键点详解

文件操作函数路径处理差异

问题描述

PHP中文件读写操作(file_put_contents, copy, file_get_contents等)与文件删除/判断操作(unlink, file_exists等)对路径处理存在差异,可能导致安全绕过。

技术原理

  • 读写操作:调用php_stream_open_wrapper_ex,最终使用tsrm_realpath标准化路径
  • 删除/判断操作:不进行路径标准化处理

绕过方法

  • Linux系统:
    • xxxxx/../test.php
    • test.php/.
  • Windows系统:
    • test.php:test
    • test.ph<
  • 伪协议:
    • php://filter/resource=1.php

示例代码

$filename = __DIR__ . '/tmp/' . $user['name'];
$data = $user['info'];
file_put_contents($filename, $data);
if(file_exists($filename)) {
    unlink($filename);  // 可被绕过
}

变量覆盖漏洞

extract()函数

extract($_GET);  // 危险:直接覆盖现有变量
extract($_GET, EXTR_SKIP);  // 安全:跳过已有变量
extract($_GET, EXTR_PREFIX_SAME, "prefix");  // 安全:添加前缀

parse_str()函数

parse_str("name=Bill&age=60");  // 危险:直接创建变量
parse_str("name=Bill&age=60", $output);  // 安全:使用数组接收

数值处理问题

intval()函数特性

  • 32位系统最大带符号范围:-2147483648到2147483647
  • 64位系统最大带符号范围:9223372036854775807
  • 截断取整(非四舍五入):intval(10.99999) → 10
  • 转换规则:遇到数字或正负号开始,遇到非数字或\0结束

浮点数精度问题

var_dump(1.000000000000000 == 1);   // TRUE
var_dump(1.0000000000000001 == 1);  // TRUE

is_numeric()与intval()差异

is_numeric("\r\n\t 0.1e2");  // TRUE
intval("\r\n\t 12");         // 12

函数比较绕过

strcmp()数组绕过

if(strcmp($_GET['a'], $_GET['b']) == 0)  // 传入数组可绕过

sha1()/md5()数组绕过

if(sha1($_GET['a']) === sha1($_GET['b']))  // 传入数组可绕过

弱类型比较问题

常见绕过示例

md5('240610708') == md5('QNKCDZO');  // TRUE (0e...)
sha1('aaroZmOk') == sha1('aaK1STfY'); // TRUE
'0010e2' == '1e3';  // TRUE
'0x1234Ab' == ' 1193131 ';  // TRUE

布尔转换规则

FALSE情况:FALSE, 0, 0.0, "", "0", array(), NULL

字符串转数值规则

  • 开头有数字:转为数字并省略后面非数字字符
  • 开头无数字:转为0
1 + "bob-1.3e3"  // 1
1 + "10 Small Pigs"  // 11

其他关键点

eregi()匹配绕过

  • 传入数组可绕过
  • 使用%00截断

变量名转换规则

点号和空格会被转为下划线:

parse_str("na.me=admin&pass wd=123", $test);
// 结果:["na_me"]=>"admin", ["pass_wd"]=>"123"

in_array()松散比较

in_array("1asd", array(1,2,3,4));       // TRUE
in_array("1asd", array(1,2,3,4), TRUE); // FALSE

htmlspecialchars()转义问题

默认不转义单引号,需使用ENT_QUOTES参数:

htmlspecialchars($input);          // 仅转义双引号
htmlspecialchars($input, ENT_QUOTES); // 转义双引号和单引号

sprintf()格式化漏洞

可以吃掉转义后的单引号:

$sql = "select * from user where username = '%\' and 1=1#'";
sprintf($sql, "admin");  // 结果:username = '' and 1=1#'

赋值运算符优先级

=优先级高于and

$c = is_numeric($a) and is_numeric($b);  // 可能绕过$b检查

URL解析差异

parse_url与libcurl差异

  • user@eval.com:80@baidu.com
    • PHP解析host:baidu.com
    • libcurl解析host:eval.com

filter_var()绕过

filter_var("0://evil.com:80;google.com:80/", FILTER_VALIDATE_URL);  // TRUE

通过file_get_contents的XSS

$url = "data://baidu.com/plain;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pgo=";
file_get_contents($url);  // 返回<script>alert(1)</script>

防御建议

  1. 对所有文件操作使用绝对路径并进行标准化处理
  2. 使用EXTR_SKIPEXTR_PREFIX_SAME参数处理extract()
  3. 对数值比较使用严格比较===
  4. strcmp()等函数添加类型检查
  5. 使用htmlspecialchars($input, ENT_QUOTES)完全转义
  6. 对URL验证使用多重检查机制
  7. 避免直接使用用户输入拼接SQL语句
  8. 对重要比较使用严格模式(如in_array的第三个参数)
PHP代码审计关键点详解 文件操作函数路径处理差异 问题描述 PHP中文件读写操作( file_put_contents , copy , file_get_contents 等)与文件删除/判断操作( unlink , file_exists 等)对路径处理存在差异,可能导致安全绕过。 技术原理 读写操作:调用 php_stream_open_wrapper_ex ,最终使用 tsrm_realpath 标准化路径 删除/判断操作:不进行路径标准化处理 绕过方法 Linux系统: xxxxx/../test.php test.php/. Windows系统: test.php:test test.ph< 伪协议: php://filter/resource=1.php 示例代码 变量覆盖漏洞 extract()函数 parse_ str()函数 数值处理问题 intval()函数特性 32位系统最大带符号范围:-2147483648到2147483647 64位系统最大带符号范围:9223372036854775807 截断取整(非四舍五入): intval(10.99999) → 10 转换规则:遇到数字或正负号开始,遇到非数字或 \0 结束 浮点数精度问题 is_ numeric()与intval()差异 函数比较绕过 strcmp()数组绕过 sha1()/md5()数组绕过 弱类型比较问题 常见绕过示例 布尔转换规则 FALSE情况: FALSE , 0 , 0.0 , "" , "0" , array() , NULL 字符串转数值规则 开头有数字:转为数字并省略后面非数字字符 开头无数字:转为0 其他关键点 eregi()匹配绕过 传入数组可绕过 使用 %00 截断 变量名转换规则 点号和空格会被转为下划线: in_ array()松散比较 htmlspecialchars()转义问题 默认不转义单引号,需使用 ENT_QUOTES 参数: sprintf()格式化漏洞 可以吃掉转义后的单引号: 赋值运算符优先级 = 优先级高于 and : URL解析差异 parse_ url与libcurl差异 user@eval.com:80@baidu.com : PHP解析host: baidu.com libcurl解析host: eval.com filter_ var()绕过 通过file_ get_ contents的XSS 防御建议 对所有文件操作使用绝对路径并进行标准化处理 使用 EXTR_SKIP 或 EXTR_PREFIX_SAME 参数处理 extract() 对数值比较使用严格比较 === 对 strcmp() 等函数添加类型检查 使用 htmlspecialchars($input, ENT_QUOTES) 完全转义 对URL验证使用多重检查机制 避免直接使用用户输入拼接SQL语句 对重要比较使用严格模式(如 in_array 的第三个参数)