PHP文件上传安全

一、文件上传的危害

在PHP项目中,提供上传功能并未在服务端对上传的文件格式进行校验,将存在巨大风险。

如果恶意攻击者利用上传漏洞上传一些webshell,则可能完全控制整个网站程序,执行系统命令,获取数据库连接字串进行操作数据库等危险操作。

二、被攻击示例

html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件漏洞测试</title>
</head>
<body>
    <form action="upload.php" method = "post" ENCTYPE="multipart/form-data">
        请上传文件
        <input type="file" name="upload_file"><br><br>
        <input type="submit" value="上传">
    </form>
</body>
</html>

 
php文件 upload.php

<?php
declare(strict_types = 1);

$uploadDir = 'uploads/';
$uploadFile = $uploadDir . $_FILES['upload_file']['name'];
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $uploadFile)) {
    echo '上传成功';
} else {
    echo '错误码为'.$_FILES['upload_file']['error'];
}

正常上传文件是没有问题的,但前面说过,这是存在漏洞的,现在我们开始模拟这个漏洞。

上传一个攻击文件 shell.php

<?php
declare(strict_types = 1);

system($_GET['shell']);

上传之后我们访问 website/上传目录/shell.php,并在后面添加shell命令进行攻击。

模拟失败原因:

1、文件目录权限不够导致上传文件失败,设置目录权限为777再测试

2、php.ini配置问题,配置项 disable_functions设置了system为禁用方法。

三、解决方案

1、选择使用对象存储存储用户上传文件

2、选择将文件存储到自己的服务器时,处理上传文件时注意以下几点:

(1)检查上传文件的类型及扩展名

(2)php.ini 配置好禁用函数 disable_functions

<?php
declare(strict_types = 1);

$uploadDir = 'uploads/';

$validExtensionList = ['png', 'jpg', 'jpeg', 'gif'];

// 获取上传文件扩展名
$fileExtension = pathinfo($_FILES['upload_file']['name'], PATHINFO_EXTENSION);

if (!in_array($fileExtension, $validExtensionList)) {
    die('上传文件类型非法');
}

$fileName = $uploadDir . getRandomString(12) . mt_rand(10000,99999) .'.'. $fileExtension;
if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $fileName)) {
    echo '上传成功,文件名为 '.$fileName;
} else {
    echo '错误码为'.$_FILES['upload_file']['error'];
}

/**
 * 生成随机字符串
 *
 * @param int $len
 * @param string|null $chars
 * @return string
 */
function getRandomString(int $length, string $chars = null) : string
{
    if (is_null($chars)) {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ23456789";
    }
    $str = '';
    $charLength = strlen($chars) - 1;
    for ($i = 0; $i < $length; $i++) {
        $str .= $chars[mt_rand(0, $charLength)];
    }
    return $str;
}