PHP代码审计系列基础文章(三)之文件上传漏洞篇
字数 1502 2025-08-11 21:26:06

PHP文件上传漏洞审计与防御指南

一、文件上传漏洞基本原理

1.1 核心概念

文件上传漏洞是指攻击者通过Web应用的上传功能,将恶意文件(如Webshell)上传到服务器并成功解析执行的安全漏洞。

漏洞存在的三个前提条件

  1. 存在文件上传功能点
  2. 上传内容可由攻击者控制
  3. 上传的文件能被服务器解析执行

1.2 文件上传业务流程

  1. 攻击者通过前端页面上传恶意文件
  2. 服务器接收文件并存储为临时文件
  3. 服务器将临时文件移动到指定位置
  4. 攻击者访问上传的文件
  5. 服务器解析执行恶意文件

1.3 利用条件

  1. Web站点具有上传功能点
  2. 上传的目标文件可被Web服务器解析
  3. 已知上传的目标文件路径及文件名
  4. 目标文件可被访问(无访问控制限制)

二、PHP文件上传实现机制

2.1 前端代码示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件上传小demo</title>
</head>
<body>
    <form action="demo.php" method="post" enctype="multipart/form-data">
        <p>请选择上传的文件:</p>
        <input class="upload_file" type="file" name="uploadFile"/>
        <input class="button" type="submit" name="submit" value="上传"/>
    </form>
</body>
</html>

关键属性说明

  • action: 指定处理上传的后端文件
  • method: 必须为POST
  • enctype: 必须为multipart/form-data
  • type="file": 定义文件上传控件

2.2 后端处理代码示例

<?php
if(is_uploaded_file($_FILES["uploadFile"]["tmp_name"])) {
    $upFile = $_FILES["uploadFile"];
    $name = $upFile["name"];
    $type = $upFile["type"];
    $size = $upFile["size"];
    $tmpName = $upFile["tmp_name"];
    
    var_dump($upFile);
    $path = "./".$name;
    move_uploaded_file($tmpName,$path);
    echo "文件上传成功";
} else {
    echo "文件上传失败";
}
?>

2.3 PHP关键函数与变量

$_FILES超全局变量

  • $_FILES['uploadFile']['name']: 客户端文件的原名称
  • $_FILES['uploadFile']['type']: 文件的MIME类型
  • $_FILES['uploadFile']['size']: 已上传文件的大小(字节)
  • $_FILES['uploadFile']['tmp_name']: 服务器端存储的临时文件名
  • $_FILES['uploadFile']['error']: 上传相关的错误代码

关键函数

  • is_uploaded_file(): 检查文件是否通过HTTP POST上传
  • move_uploaded_file(): 将临时文件移动到新位置

重要注意事项

  • 上传的文件默认会被存储为临时文件(如phpBD6C.tmp)
  • 必须使用move_uploaded_file()保存文件,否则脚本执行完毕后临时文件会被删除

三、文件上传检测类型与绕过方法

3.1 前端JS检测示例

<script type="text/javascript">
function checkFile() {
    var file = document.getElementsByName('uploadFile')[0].value;
    if (file == null || file == "") {
        alert("请选择要上传的文件!");
        return false;
    }
    
    var allow_ext = ".jpg|.png|.gif";
    var ext_name = file.substring(file.lastIndexOf("."));
    
    if (allow_ext.indexOf(ext_name) == -1) {
        var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
        alert(errMsg);
        return false;
    }
}
</script>

3.2 绕过前端检测的方法

  1. 修改文件后缀名为允许的类型(如.jpg)
  2. 使用Burp Suite等工具拦截请求
  3. 将文件名改回恶意后缀(如.php)
  4. 放行请求绕过前端验证

四、文件上传漏洞审计要点

4.1 重点审计对象

  1. 文件类型检测代码

    • 前端JS验证
    • 后端MIME类型检查
    • 文件扩展名检查
    • 文件内容检查
  2. 关键PHP函数

    • move_uploaded_file()
    • $_FILES超全局变量的使用
  3. 文件存储处理

    • 文件名生成逻辑
    • 存储路径控制
    • 权限设置

4.2 常见漏洞模式

  1. 无任何检测的直接上传
  2. 仅前端检测无后端验证
  3. 黑名单机制不完善
  4. 文件类型检测可被绕过
  5. 文件内容未进行安全检查

五、防御措施

5.1 基础防御方案

  1. 使用白名单限制文件扩展名
  2. 同时在前端和后端进行验证
  3. 重命名上传文件(避免使用用户提供的文件名)
  4. 限制上传文件大小
  5. 设置适当的文件权限

5.2 进阶防御措施

  1. 对文件内容进行检测(如检查图片文件头)
  2. 将上传文件存储在非Web可访问目录
  3. 使用单独的子域名托管用户上传内容
  4. 定期扫描上传目录中的可疑文件
  5. 禁用危险的文件执行权限(如PHP文件)

5.3 安全代码示例

<?php
$allowed_ext = array('jpg', 'png', 'gif');
$max_size = 1024 * 1024; // 1MB

if(is_uploaded_file($_FILES["uploadFile"]["tmp_name"])) {
    $file = $_FILES["uploadFile"];
    
    // 检查文件大小
    if($file['size'] > $max_size) {
        die("文件大小超过限制");
    }
    
    // 获取文件扩展名
    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    
    // 检查扩展名
    if(!in_array($ext, $allowed_ext)) {
        die("不允许的文件类型");
    }
    
    // 生成随机文件名
    $new_name = uniqid().'.'.$ext;
    $path = "./uploads/".$new_name;
    
    // 移动文件
    if(move_uploaded_file($file['tmp_name'], $path)) {
        echo "文件上传成功";
    } else {
        echo "文件保存失败";
    }
} else {
    echo "文件上传失败";
}
?>

六、总结

文件上传漏洞是Web应用中最常见也最危险的安全问题之一。审计时需重点关注文件类型检测的实现方式、文件存储处理逻辑以及相关函数的使用情况。防御时应采用多层次的安全措施,包括但不限于白名单验证、文件重命名、内容检测和权限控制等。

PHP文件上传漏洞审计与防御指南 一、文件上传漏洞基本原理 1.1 核心概念 文件上传漏洞是指攻击者通过Web应用的上传功能,将恶意文件(如Webshell)上传到服务器并成功解析执行的安全漏洞。 漏洞存在的三个前提条件 : 存在文件上传功能点 上传内容可由攻击者控制 上传的文件能被服务器解析执行 1.2 文件上传业务流程 攻击者通过前端页面上传恶意文件 服务器接收文件并存储为临时文件 服务器将临时文件移动到指定位置 攻击者访问上传的文件 服务器解析执行恶意文件 1.3 利用条件 Web站点具有上传功能点 上传的目标文件可被Web服务器解析 已知上传的目标文件路径及文件名 目标文件可被访问(无访问控制限制) 二、PHP文件上传实现机制 2.1 前端代码示例 关键属性说明 : action : 指定处理上传的后端文件 method : 必须为POST enctype : 必须为 multipart/form-data type="file" : 定义文件上传控件 2.2 后端处理代码示例 2.3 PHP关键函数与变量 $_ FILES超全局变量 $_FILES['uploadFile']['name'] : 客户端文件的原名称 $_FILES['uploadFile']['type'] : 文件的MIME类型 $_FILES['uploadFile']['size'] : 已上传文件的大小(字节) $_FILES['uploadFile']['tmp_name'] : 服务器端存储的临时文件名 $_FILES['uploadFile']['error'] : 上传相关的错误代码 关键函数 is_uploaded_file() : 检查文件是否通过HTTP POST上传 move_uploaded_file() : 将临时文件移动到新位置 重要注意事项 : 上传的文件默认会被存储为临时文件(如phpBD6C.tmp) 必须使用 move_uploaded_file() 保存文件,否则脚本执行完毕后临时文件会被删除 三、文件上传检测类型与绕过方法 3.1 前端JS检测示例 3.2 绕过前端检测的方法 修改文件后缀名为允许的类型(如.jpg) 使用Burp Suite等工具拦截请求 将文件名改回恶意后缀(如.php) 放行请求绕过前端验证 四、文件上传漏洞审计要点 4.1 重点审计对象 文件类型检测代码 前端JS验证 后端MIME类型检查 文件扩展名检查 文件内容检查 关键PHP函数 move_uploaded_file() $_FILES 超全局变量的使用 文件存储处理 文件名生成逻辑 存储路径控制 权限设置 4.2 常见漏洞模式 无任何检测的直接上传 仅前端检测无后端验证 黑名单机制不完善 文件类型检测可被绕过 文件内容未进行安全检查 五、防御措施 5.1 基础防御方案 使用白名单限制文件扩展名 同时在前端和后端进行验证 重命名上传文件(避免使用用户提供的文件名) 限制上传文件大小 设置适当的文件权限 5.2 进阶防御措施 对文件内容进行检测(如检查图片文件头) 将上传文件存储在非Web可访问目录 使用单独的子域名托管用户上传内容 定期扫描上传目录中的可疑文件 禁用危险的文件执行权限(如PHP文件) 5.3 安全代码示例 六、总结 文件上传漏洞是Web应用中最常见也最危险的安全问题之一。审计时需重点关注文件类型检测的实现方式、文件存储处理逻辑以及相关函数的使用情况。防御时应采用多层次的安全措施,包括但不限于白名单验证、文件重命名、内容检测和权限控制等。