代码审计---文件上传
字数 1661 2025-08-09 22:00:46

代码审计之文件上传漏洞详解

文件上传漏洞概述

文件上传漏洞是指Web应用程序在处理用户上传文件时,未对上传文件的类型、内容、扩展名等进行严格验证,导致攻击者可以上传恶意文件(如Webshell、病毒、木马等)到服务器上,从而获取系统权限或进行其他恶意操作。

常见文件上传漏洞类型

1. 前端验证绕过

漏洞原理:仅依赖JavaScript进行文件类型验证,攻击者可以禁用JS或修改前端代码绕过验证。

审计要点

  • 检查是否仅在前端使用accept属性或JS验证文件类型
  • 查看是否有服务端验证逻辑

2. MIME类型验证绕过

漏洞原理:仅检查HTTP头中的Content-Type,攻击者可伪造MIME类型。

审计要点

  • 检查代码中是否仅验证$_FILES['file']['type']
  • 是否使用mime_content_type()或文件头验证

3. 文件扩展名验证不严

漏洞原理

  • 黑名单机制不完整(遗漏可执行扩展名)
  • 大小写绕过(如.PHp)
  • 特殊后缀(如.php5, .phtml)
  • 双扩展名绕过(如.php.jpg)

审计要点

  • 检查扩展名验证是黑名单还是白名单
  • 是否处理了大小写、点号、空格等特殊情况
  • 是否使用pathinfo()explode()分割文件名

4. 文件内容验证缺失

漏洞原理:不检查文件实际内容,仅凭扩展名判断文件类型。

审计要点

  • 是否检查文件头标识(如FFD8FF为JPEG)
  • 是否使用getimagesize()验证图片文件
  • 是否对文件内容进行二次渲染

5. 目录路径可控

漏洞原理:上传路径或文件名用户可控,可能导致目录穿越。

审计要点

  • 检查上传路径是否拼接用户输入
  • 是否过滤../等路径穿越字符
  • 是否使用basename()处理文件名

6. 条件竞争漏洞

漏洞原理:文件上传后未立即重命名或检查,攻击者可利用时间差执行恶意文件。

审计要点

  • 检查上传后处理流程是否存在时间窗口
  • 是否先保存后验证

代码审计实战要点

1. 定位文件上传功能

查找以下关键字:

$_FILES
move_uploaded_file()
<input type="file">
enctype="multipart/form-data"

2. 验证逻辑分析

检查是否存在以下安全措施:

// 白名单验证
$allowedExts = array("gif", "jpeg", "jpg", "png");
$extension = end(explode(".", $_FILES["file"]["name"]));

// 文件类型验证
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);

// 文件内容验证
if (!getimagesize($_FILES["file"]["tmp_name"])) {
    die("不是有效的图片文件");
}

// 重命名处理
$new_name = md5(uniqid()).'.'.$extension;

3. 常见危险函数

  • move_uploaded_file() - 未经验证直接保存文件
  • copy() - 可能用于复制上传文件
  • file_put_contents() - 可能直接写入用户可控内容

防御措施

1. 严格的验证策略

// 白名单验证扩展名
$allowed = ['jpg', 'png', 'gif'];
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed)) {
    die('不允许的文件类型');
}

// 验证MIME类型
$allowedMime = ['image/jpeg', 'image/png', 'image/gif'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
if (!in_array($mime, $allowedMime)) {
    die('非法的文件类型');
}

// 验证文件内容
if (!getimagesize($_FILES['file']['tmp_name'])) {
    die('不是有效的图片文件');
}

2. 安全处理措施

  • 上传目录设置为不可执行
  • 重命名上传文件(如使用MD5随机名称)
  • 去除文件元数据(对图片进行二次渲染)
  • 设置正确的文件权限
  • 使用独立域名存放上传文件

3. 其他安全建议

  • 限制上传文件大小
  • 记录上传日志
  • 对压缩包文件进行病毒扫描
  • 定期审计服务器上的可疑文件

实战案例

案例1:简单文件上传漏洞

<?php
if (isset($_POST['submit'])) {
    $file = $_FILES['file'];
    move_uploaded_file($file['tmp_name'], 'uploads/'.$file['name']);
    echo "文件上传成功!";
}
?>

漏洞分析

  • 无任何验证措施
  • 文件名和内容完全可控
  • 可直接上传PHP等脚本文件

案例2:不完整的黑名单验证

<?php
$blacklist = array('.php', '.asp', '.jsp');
$file_name = $_FILES['file']['name'];
foreach ($blacklist as $item) {
    if (preg_match('/'.$item.'$/i', $file_name)) {
        die('不允许的文件类型');
    }
}
move_uploaded_file($_FILES['file']['tmp_name'], 'uploads/'.$file_name);
?>

绕过方法

  • 使用.php5, .phtml等扩展名
  • 使用大小写如.PhP
  • 使用双扩展名如shell.php.jpg

案例3:安全的文件上传实现

<?php
$allowed_ext = ['jpg', 'png', 'gif'];
$allowed_mime = ['image/jpeg', 'image/png', 'image/gif'];
$max_size = 1024 * 1024; // 1MB

if (isset($_POST['submit'])) {
    $file = $_FILES['file'];
    
    // 验证错误代码
    if ($file['error'] !== UPLOAD_ERR_OK) {
        die('上传错误: '.$file['error']);
    }
    
    // 验证文件大小
    if ($file['size'] > $max_size) {
        die('文件过大');
    }
    
    // 验证扩展名
    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    if (!in_array($ext, $allowed_ext)) {
        die('不允许的文件类型');
    }
    
    // 验证MIME类型
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $mime = finfo_file($finfo, $file['tmp_name']);
    if (!in_array($mime, $allowed_mime)) {
        die('非法的文件类型');
    }
    
    // 验证图片内容
    if (!getimagesize($file['tmp_name'])) {
        die('不是有效的图片文件');
    }
    
    // 安全重命名
    $new_name = md5(uniqid()).'.'.$ext;
    $upload_path = 'uploads/'.$new_name;
    
    if (move_uploaded_file($file['tmp_name'], $upload_path)) {
        echo '文件上传成功!';
    } else {
        echo '文件保存失败';
    }
}
?>

高级绕过技巧

1. 图片马制作

将Webshell代码插入图片的注释或EXIF数据中:

exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpg -o webshell.php

2. 文件头伪造

在PHP文件开头添加图片文件头:

GIF89a
<?php phpinfo(); ?>

3. 条件竞争利用

当服务器先保存后验证时,快速访问上传的临时文件:

import requests
while True:
    r = requests.get('http://target/uploads/temp.php')
    if 'success' in r.text:
        break

自动化审计工具

  1. RIPS - PHP代码静态分析工具
  2. Fortify - 商业代码审计工具
  3. Checkmarx - 商业SAST解决方案
  4. SonarQube - 开源代码质量检测平台

总结

文件上传漏洞是Web安全中最常见也最危险的漏洞之一。在代码审计过程中,需要重点关注:

  1. 验证逻辑是否全面(扩展名、MIME、内容)
  2. 是否使用白名单而非黑名单
  3. 文件保存处理是否安全
  4. 是否存在条件竞争问题
  5. 上传目录权限设置是否合理

通过全面的验证和严格的安全措施,可以有效防范文件上传漏洞带来的安全风险。

代码审计之文件上传漏洞详解 文件上传漏洞概述 文件上传漏洞是指Web应用程序在处理用户上传文件时,未对上传文件的类型、内容、扩展名等进行严格验证,导致攻击者可以上传恶意文件(如Webshell、病毒、木马等)到服务器上,从而获取系统权限或进行其他恶意操作。 常见文件上传漏洞类型 1. 前端验证绕过 漏洞原理 :仅依赖JavaScript进行文件类型验证,攻击者可以禁用JS或修改前端代码绕过验证。 审计要点 : 检查是否仅在前端使用 accept 属性或JS验证文件类型 查看是否有服务端验证逻辑 2. MIME类型验证绕过 漏洞原理 :仅检查HTTP头中的Content-Type,攻击者可伪造MIME类型。 审计要点 : 检查代码中是否仅验证 $_FILES['file']['type'] 是否使用 mime_content_type() 或文件头验证 3. 文件扩展名验证不严 漏洞原理 : 黑名单机制不完整(遗漏可执行扩展名) 大小写绕过(如.PHp) 特殊后缀(如.php5, .phtml) 双扩展名绕过(如.php.jpg) 审计要点 : 检查扩展名验证是黑名单还是白名单 是否处理了大小写、点号、空格等特殊情况 是否使用 pathinfo() 或 explode() 分割文件名 4. 文件内容验证缺失 漏洞原理 :不检查文件实际内容,仅凭扩展名判断文件类型。 审计要点 : 是否检查文件头标识(如 FFD8FF 为JPEG) 是否使用 getimagesize() 验证图片文件 是否对文件内容进行二次渲染 5. 目录路径可控 漏洞原理 :上传路径或文件名用户可控,可能导致目录穿越。 审计要点 : 检查上传路径是否拼接用户输入 是否过滤 ../ 等路径穿越字符 是否使用 basename() 处理文件名 6. 条件竞争漏洞 漏洞原理 :文件上传后未立即重命名或检查,攻击者可利用时间差执行恶意文件。 审计要点 : 检查上传后处理流程是否存在时间窗口 是否先保存后验证 代码审计实战要点 1. 定位文件上传功能 查找以下关键字: 2. 验证逻辑分析 检查是否存在以下安全措施: 3. 常见危险函数 move_uploaded_file() - 未经验证直接保存文件 copy() - 可能用于复制上传文件 file_put_contents() - 可能直接写入用户可控内容 防御措施 1. 严格的验证策略 2. 安全处理措施 上传目录设置为不可执行 重命名上传文件(如使用MD5随机名称) 去除文件元数据(对图片进行二次渲染) 设置正确的文件权限 使用独立域名存放上传文件 3. 其他安全建议 限制上传文件大小 记录上传日志 对压缩包文件进行病毒扫描 定期审计服务器上的可疑文件 实战案例 案例1:简单文件上传漏洞 漏洞分析 : 无任何验证措施 文件名和内容完全可控 可直接上传PHP等脚本文件 案例2:不完整的黑名单验证 绕过方法 : 使用 .php5 , .phtml 等扩展名 使用大小写如 .PhP 使用双扩展名如 shell.php.jpg 案例3:安全的文件上传实现 高级绕过技巧 1. 图片马制作 将Webshell代码插入图片的注释或EXIF数据中: 2. 文件头伪造 在PHP文件开头添加图片文件头: 3. 条件竞争利用 当服务器先保存后验证时,快速访问上传的临时文件: 自动化审计工具 RIPS - PHP代码静态分析工具 Fortify - 商业代码审计工具 Checkmarx - 商业SAST解决方案 SonarQube - 开源代码质量检测平台 总结 文件上传漏洞是Web安全中最常见也最危险的漏洞之一。在代码审计过程中,需要重点关注: 验证逻辑是否全面(扩展名、MIME、内容) 是否使用白名单而非黑名单 文件保存处理是否安全 是否存在条件竞争问题 上传目录权限设置是否合理 通过全面的验证和严格的安全措施,可以有效防范文件上传漏洞带来的安全风险。