[红日安全]代码审计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 解题思路
- 利用
in_array()非严格比较绕过白名单检查 - 构造Payload时不使用被过滤的关键字
- 可能的Payload示例:
id=1 AND (SELECT flag FROM flag)
6. 最佳实践总结
- 始终使用严格模式:
in_array($value, $array, true) - 输入验证与输出编码:验证所有用户输入,编码所有输出
- 最小权限原则:数据库连接使用最小必要权限
- 参数化查询:使用预处理语句替代SQL拼接
- 深度防御:多层安全措施,不依赖单一防护
- 安全编码培训:提高开发人员安全意识
7. 扩展思考
- PHP弱类型系统带来的其他安全问题
- 自动化代码审计工具如何检测此类漏洞
- 在现代化PHP框架中如何避免此类问题
- 其他存在类似问题的PHP函数(如
array_search()等)
通过深入理解in_array()函数的工作原理和安全风险,开发人员可以编写更安全的代码,有效预防此类漏洞的发生。