PHP 文件上传漏洞原理与安全防御写法

iT日记 网络攻防 26
PHP 文件上传漏洞原理与安全防御写法-第1张图片-iT日记

文件上传漏洞是网站被挂马、被植入 webshell、服务器沦陷最常见的入口。不管是 ZBlog 主题插件、自定义表单、后台文件上传、头像上传、附件上传,只要自己写上传功能,一旦过滤不严,攻击者就能传木马后门,直接控制网站甚至整台服务器。

本文讲透上传漏洞原理、常见绕过方式,给你可直接商用的安全上传代码,PHP 项目、CMS 二次开发都能直接套用。

一、文件上传漏洞核心原理

原理一句话:后端没有严格校验文件类型、后缀、MIME、文件内容,导致允许上传 php、phtml 等可执行脚本,被上传木马并访问执行。

常见危害:

上传 Webshell 后门


篡改首页、跳转博彩


拖库下载整站数据库


植入挖矿程序、内网横向渗透


拿下服务器权限


二、常见简陋过滤方式(全部可绕过)

1. 只判断后缀(不安全)

只拦截 .php,改成 .php5、.phtml、.phar 就能绕过。

2. 只校验 MIME 类型

Content-Type 可伪造,伪造图片类型轻松绕过。

3. 前端 JS 校验

前端限制完全没用,抓包改包直接绕过。

4. 只改文件名不检测内容

伪装图片后缀,里面仍是 PHP 木马,配合解析漏洞照样执行。

三、专业安全防御五大原则

  1. 禁止上传目录执行 PHP(Nginx 配置拦截)

  2. 后端双重校验:后缀 + MIME + 文件真实内容

  3. 上传文件统一重命名,不保留原文件名

  4. 上传目录独立隔离,不放在网站根程序目录

  5. 不允许用户自定义存放路径和文件名

四、Nginx 配置:上传目录禁止执行脚本(第一道防线)

在站点 Nginx 配置里加入,上传目录直接禁 PHP 执行,就算传了木马也跑不起来。
location ~* /upload/.*\.(php|php5|phtml|phar|asp|aspx) {
    deny all;
}

把 /upload/ 换成你实际的上传文件夹路径。

五、PHP 安全上传完整代码(可直接上线)

1. 安全上传封装函数

<?php
// 允许的安全后缀
$allow_suffix = ['jpg','jpeg','png','gif','webp','bmp'];
// 允许的MIME类型
$allow_mime = [
    'image/jpeg',
    'image/png',
    'image/gif',
    'image/webp'
];

function get_file_suffix($filename){
    return strtolower(pathinfo($filename,PATHINFO_EXTENSION));
}

// 安全上传主函数
function safe_upload($file,$save_path = 'upload/'){
    global $allow_suffix,$allow_mime;

    // 1. 创建目录不存在则新建
    if(!is_dir($save_path)){
        mkdir($save_path,0755,true);
    }

    $filename = $file['name'];
    $tmp_name = $file['tmp_name'];
    $file_mime = $file['type'];
    $suffix = get_file_suffix($filename);

    // 2. 校验后缀
    if(!in_array($suffix,$allow_suffix)){
        return ['code'=>0,'msg'=>'文件后缀不允许'];
    }

    // 3. 校验MIME类型
    if(!in_array($file_mime,$allow_mime)){
        return ['code'=>0,'msg'=>'文件类型非法'];
    }

    // 4. 校验文件真实内容(防止伪装图片)
    $imginfo = getimagesize($tmp_name);
    if(!$imginfo){
        return ['code'=>0,'msg'=>'不是合法图片文件'];
    }

    // 5. 随机重命名,防止覆盖和后门探测
    $new_name = date('YmdHis').mt_rand(1000,9999).'.'.$suffix;
    $save_file = $save_path.$new_name;

    // 6. 移动保存
    if(move_uploaded_file($tmp_name,$save_file)){
        return ['code'=>1,'msg'=>'上传成功','path'=>$save_file];
    }else{
        return ['code'=>0,'msg'=>'文件保存失败'];
    }
}
?>

2. 前端表单示例

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="upfile" />
    <button type="submit">上传</button>
</form>

3. 后端处理 upload.php

<?php
include "safe_upload.php";

if(!empty($_FILES['upfile'])){
    $res = safe_upload($_FILES['upfile']);
    if($res['code'] == 1){
        echo "上传成功:".$res['path'];
    }else{
        echo "失败:".$res['msg'];
    }
}
?>

六、额外加固技巧

1、关闭 PHP 危险函数

禁用 file_put_contents、fopen、copy 等不必要文件函数,减少恶意写入。

2、上传目录设置权限

Linux 下设置目录权限 0755,禁止写入执行权限。

3、定期扫描上传目录

用宝塔木马扫描,定时检测 upload 目录是否出现可疑 PHP 文件。

3、不用原生上传,尽量用成熟组件

ZBlog、MetInfo 自带上传类已经做了防护,尽量不要自己手写简易上传。

七、总结

文件上传防护记住三步就够:

1、Nginx 上传目录禁止执行脚本

2、PHP 同时校验 后缀 + MIME + 文件真实内容

3、文件随机重命名、隔离目录、不保留原名

按本文代码和配置部署,能拦截 99% 上传漏洞与 Webshell 植入,

标签: PHP 文件上传漏洞 上传漏洞防御 PHP 安全上传代码 网站防挂马配置