[红日安全]代码审计Day1 - in_array函数缺陷
字数 946 2025-08-18 11:37:33

PHP代码审计:in_array函数缺陷与安全风险

1. in_array函数基础

1.1 函数定义

in_array()是PHP中用于检查数组中是否存在某个值的函数,其定义如下:

bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )

1.2 参数说明

  • $needle: 要搜索的值
  • $haystack: 要搜索的数组
  • $strict: 可选参数,默认为FALSE,决定是否进行严格类型检查

1.3 工作模式

  • 非严格模式(默认): 会进行松散比较,可能导致类型转换
  • 严格模式: 同时比较值和类型,避免意外类型转换

2. 安全漏洞分析

2.1 漏洞原理

in_array()函数在非严格模式下使用时,PHP会进行自动类型转换,可能导致安全绕过:

$whitelist = range(1, 24);
$filename = '7shell.php';

// 非严格比较
if (in_array($filename, $whitelist)) {
    // 这里会被绕过,因为'7shell.php'会被强制转换为数字7
}

2.2 实际案例:Piwigo 2.7.1 SQL注入

在Piwigo 2.7.1中,存在以下漏洞代码:

// include/functions_rate.inc.php
function rate_picture($image_id, $rate)
{
    global $conf;
    
    if (!in_array($rate, $conf['rate_items'])) {
        die('Invalid rate');
    }
    
    // 直接拼接$rate变量导致SQL注入
    $query = 'INSERT INTO '.RATES_TABLE.' (user_id,anonymous_id,element_id,rate,date)';
    $query.= ' VALUES ('.$_user['id'].',\''.$anonymous_id.'\','.$image_id.','.$rate.',NOW())';
}

攻击者可构造如下Payload:

rate=1,1 and if(ascii(substr((select database()),1,1))=112,1,sleep(3)));#

3. 漏洞利用场景

3.1 文件上传绕过

当使用数字白名单验证文件名时:

$allowed = range(1, 24);
$filename = $_FILES['file']['name'];

if (in_array($filename, $allowed)) {
    // 允许上传
}

攻击者可上传7shell.php绕过检查。

3.2 SQL注入

当使用非严格模式验证用户输入并直接拼接SQL时:

$rate = $_POST['rate'];
$allowed = array(0,1,2,3,4,5);

if (in_array($rate, $allowed)) {
    // 直接拼接导致SQL注入
    $sql = "UPDATE table SET rate = $rate WHERE id = 1";
}

4. 修复方案

4.1 启用严格模式

if (in_array($value, $whitelist, true)) {
    // 严格比较
}

4.2 类型强制转换

$value = intval($value);
if (in_array($value, $whitelist)) {
    // 已确保类型一致
}

4.3 正则表达式验证

if (preg_match('/^\d+$/', $value) && in_array($value, $whitelist)) {
    // 双重验证
}

4.4 白名单设计改进

// 使用字符串白名单而非数字
$allowed = array('1', '2', '3', '4', '5');
if (in_array((string)$value, $allowed, true)) {
    // 更安全的验证
}

5. CTF题目分析

5.1 题目代码

// index.php
$whitelist = range(1, $row['COUNT(*)']);
$id = stop_hack($_GET['id']);

if (!in_array($id, $whitelist)) {
    die("id $id is not in whitelist.");
}

$sql = "SELECT * FROM users WHERE id=$id";

5.2 过滤函数

function stop_hack($value){
    $pattern = "insert|delete|or|concat|concat_ws|group_concat|join|floor|union|into|load_file|outfile|dumpfile|sub|hex|file_put_contents|fwrite|curl|system|eval";
    $back_list = explode("|",$pattern);
    foreach($back_list as $hack){
        if(preg_match("/$hack/i", $value)) die("$hack detected!");
    }
    return $value;
}

5.3 解题思路

  1. 利用in_array()非严格比较绕过白名单检查
  2. 构造Payload时不使用被过滤的关键字
  3. 可能的Payload示例:
    id=1 AND (SELECT flag FROM flag)
    

6. 最佳实践总结

  1. 始终使用严格模式in_array($value, $array, true)
  2. 输入验证与输出编码:验证所有用户输入,编码所有输出
  3. 最小权限原则:数据库连接使用最小必要权限
  4. 参数化查询:使用预处理语句替代SQL拼接
  5. 深度防御:多层安全措施,不依赖单一防护
  6. 安全编码培训:提高开发人员安全意识

7. 扩展思考

  1. PHP弱类型系统带来的其他安全问题
  2. 自动化代码审计工具如何检测此类漏洞
  3. 在现代化PHP框架中如何避免此类问题
  4. 其他存在类似问题的PHP函数(如array_search()等)

通过深入理解in_array()函数的工作原理和安全风险,开发人员可以编写更安全的代码,有效预防此类漏洞的发生。

PHP代码审计:in_ array函数缺陷与安全风险 1. in_ array函数基础 1.1 函数定义 in_array() 是PHP中用于检查数组中是否存在某个值的函数,其定义如下: 1.2 参数说明 $needle : 要搜索的值 $haystack : 要搜索的数组 $strict : 可选参数,默认为FALSE,决定是否进行严格类型检查 1.3 工作模式 非严格模式 (默认): 会进行松散比较,可能导致类型转换 严格模式 : 同时比较值和类型,避免意外类型转换 2. 安全漏洞分析 2.1 漏洞原理 当 in_array() 函数在非严格模式下使用时,PHP会进行自动类型转换,可能导致安全绕过: 2.2 实际案例:Piwigo 2.7.1 SQL注入 在Piwigo 2.7.1中,存在以下漏洞代码: 攻击者可构造如下Payload: 3. 漏洞利用场景 3.1 文件上传绕过 当使用数字白名单验证文件名时: 攻击者可上传 7shell.php 绕过检查。 3.2 SQL注入 当使用非严格模式验证用户输入并直接拼接SQL时: 4. 修复方案 4.1 启用严格模式 4.2 类型强制转换 4.3 正则表达式验证 4.4 白名单设计改进 5. CTF题目分析 5.1 题目代码 5.2 过滤函数 5.3 解题思路 利用 in_array() 非严格比较绕过白名单检查 构造Payload时不使用被过滤的关键字 可能的Payload示例: 6. 最佳实践总结 始终使用严格模式 : in_array($value, $array, true) 输入验证与输出编码 :验证所有用户输入,编码所有输出 最小权限原则 :数据库连接使用最小必要权限 参数化查询 :使用预处理语句替代SQL拼接 深度防御 :多层安全措施,不依赖单一防护 安全编码培训 :提高开发人员安全意识 7. 扩展思考 PHP弱类型系统带来的其他安全问题 自动化代码审计工具如何检测此类漏洞 在现代化PHP框架中如何避免此类问题 其他存在类似问题的PHP函数(如 array_search() 等) 通过深入理解 in_array() 函数的工作原理和安全风险,开发人员可以编写更安全的代码,有效预防此类漏洞的发生。