PHP上传漏洞如何修复

wen PHP项目 2

本文目录导读:

PHP上传漏洞如何修复

  1. 第一步:代码层面修复(核心)
  2. 第二步:服务器配置层面修复(零容忍)
  3. 第三步:进阶防御措施
  4. 第四步:紧急修复万能脚本(快速防范)
  5. 总结清单(自查表)

PHP 上传漏洞通常涉及文件类型绕过、路径穿越、文件内容恶意以及服务器配置缺陷,修复需要从代码层面服务器配置层面双管齐下。

以下是系统的修复方案,按严重程度和优先级排序:

第一步:代码层面修复(核心)

严禁使用客户端 MIME 类型校验(最容易被绕过)

不要信任 $_FILES['file']['type']$_FILES['file']['name'] 的后缀名,这些皆可被伪造。

使用服务端白名单校验(必须做)

错误的做法: 黑名单(如禁止 .php.asp),因为容易漏掉 .phtml.php5.htaccess 等。

正确的做法: 只允许特定扩展名(白名单)。

$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
$extension = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (!in_array($extension, $allowed_extensions, true)) {
    die('非法文件类型');
}

校验文件真实内容(绕过后缀名欺骗)

仅检查扩展名不够,攻击者可能把 PHP 代码嵌入图片中,需要使用 getimagesize()finfo 检查文件头。

// 推荐使用 Fileinfo 扩展
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
$allowed_mime = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mime, $allowed_mime, true)) {
    die('文件内容不合法');
}

⚠️ 特别提醒: 如果是图片上传,处理完校验后,建议使用 GD 库或 Imagick 重新重绘图片(去除隐含的 webshell 代码)。

重命名文件(彻底切断执行通道)

用户上传的文件必须重命名,不允许使用用户提供的原始文件名

// 生成随机文件名,掐断扩展名可控的路径
$new_name = uniqid('img_', true) . '.' . $extension;

存储到独立目录,并禁止执行权限

将上传文件放到 Web 根目录之外,并通过单独的脚本(如 download.php)读取输出;如果必须在 Web 目录下,确保该目录下 php 引擎不可解析。

防止路径穿越

不要直接把文件名拼接进路径,使用 basename() 过滤,或者像第 4 点一样重命名。


第二步:服务器配置层面修复(零容忍)

目标: 即使黑客上传了 shell.php,服务器也不执行它。

Nginx + PHP-FPM 配置

确保在 nginx.conflocation 块中,明确指定上传目录不允许解析 PHP。

# 假设上传目录是 /uploads/
location ~* ^/uploads/.*\.(php|php5|phtml)$ {
    deny all; # 或者 return 403;
}

Apache 配置

<Directory> 指令中,或者放入 .htaccess(虽然不推荐用 .htaccess,但若无法修改主配置可用):

<FilesMatch "\.(?i:php|php5|phtml|phar)$">
    Require all denied
</FilesMatch>

放在上传目录的 .htaccess 中,禁止一切 PHP 执行:

php_flag engine off
RemoveHandler .php .phtml .php5

禁用危险的 PHP 函数(纵深防御)

php.ini 中,强烈建议关闭危险函数,即使文件被上传成功,也无法执行系统命令:

disable_functions = system, exec, passthru, shell_exec, popen, proc_open, pcntl_exec, ini_alter, symlink, allow_url_fopen

第三步:进阶防御措施

文件大小限制

限制 post_max_sizeupload_max_filesize,防止上传超大文件消耗资源导致 DoS。

随机目录存储

不要把所有文件放在同一个目录,按日期或随机数分目录存储,增加攻击者猜测路径的难度。

文件名 & 路径二次检查

使用 realpath() 检查最终存储路径是否越界。


第四步:紧急修复万能脚本(快速防范)

如果你已经存在漏洞且被攻击过,临时处理可以这样拦截:

// 在 upload 处理文件的最顶端加这一段逻辑
$bad_ext = ['php', 'php3', 'php4', 'php5', 'phtml', 'pht', 'phar', 'asp', 'aspx', 'jsp'];
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (in_array($ext, $bad_ext)) {
    http_response_code(403);
    exit('Access Denied');
}
// 同时检查 Header 和伪造的 GIF89a 图片头
$content = file_get_contents($_FILES['file']['tmp_name']);
if (preg_match('/<\?php/i', $content)) {
    exit('非法内容');
}

总结清单(自查表)

检查项 是否已做
服务端扩展名白名单校验
finfo MIME 内容校验
文件重命名(不用原始文件名)
上传目录禁止解析 PHP
图片二次渲染(重绘)
disable_functions 配置
目录外存储 / 随机目录

最核心原则: 存储与执行分离,你的代码无论过滤得多么严密,只要最终文件落在 Web 根目录且可执行,就存在风险。确保服务器不执行上传目录中的任何脚本文件是最终的底线。

抱歉,评论功能暂时关闭!