025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第1张图片

#知识点:

1、PHP文件管理-下载&删除功能实现
2、PHP文件管理-编辑&包含功能实现

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第2张图片

演示案例:

➢文件管理模块-加工后续-编辑&删除&下载&包含

#PHP文件操作安全

文件包含,文件上传,文件下载,文件删除,文件写入,文件遍历

#文件包含:

include() 在错误发生后脚本继续执行
require() 在错误发生后脚本停止执行
include_once() 如果已经包含,则不再执行
require_once() 如果已经包含,则不再执行

  • 当使用include()包含html页面,就可以直接跳转到上传页面

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第3张图片

出现的:函数安全漏洞include ($_GET['page']); 可以通过改变路由访问的方式,访问到自己想要获取的资产网址

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第4张图片

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第5张图片

#文件上传:

1、无过滤机制
2、黑名单过滤机制
3、白名单过滤机制
4、文件类型过滤机制

架构:
1、上传至服务器本身的存储磁盘(源码在一起)
2、云产品OSS存储:云存储服务,旨在提高访问速度 对象去存储文件(泄漏安全)
3、把文件上传到其他域名,如:www.xiaodi8.com->upload.xiaodi8.com

影响:
上传的文件或解析的文件均来自于OSS资源,无法解析,单独存储
1、修复上传安全
2、文件解析不一样
3、但Accesskey隐患

OSS存储只是单纯的储存数据资源,没有代码执行环境,即使上传了后门脚本,也无法解析,相对于直接上传到网站服务器上,更加安全。

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第6张图片

$_FILES:PHP中一个预定义的超全局变量,用于在上传文件时从客户端接收文件,并将其保存到服务器上。它是一个包含上传文件信息的数组,包括文件名、类型、大小、临时文件名等信息。
$_FILES[“表单值”][“name”] 获取上传文件原始名称
$_FILES[“表单值”][“type”] 获取上传文件MIME类型
$_FILES[“表单值”][“size”] 获取上传文件字节单位大小
$_FILES[“表单值”][“tmp_name”] 获取上传的临时副本文件名
$_FILES[“表单值”][“error”] 获取上传时发生的错误代码
move_uploaded_file() 将上传的文件移动到指定位置的函数

#文件显示:

1.打开目录读取文件列表
2.递归循环读取文件列表
3.判断是文件还是文件夹
4.PHP.INI目录访问控制

is_dir() 函数用于检查指定的路径是否是一个目录
opendir() 函数用于打开指定的目录,返回句柄,用来读取目录中的文件和子目录
readdir() 函数用于从打开的目录句柄中读取目录中的文件和子目录
open_basedir:PHP.INI中的设置用来控制脚本程序访问目录
ini_set(‘open_basedir’,DIR); 设置配置文件中,只能访问本目录

完整实现文件显示

  • 首先创建文件filemanage.php,然后创建有关html页面,并从网上找文件夹和文件的图片,保存至新创建的img目录

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第7张图片

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第8张图片

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第9张图片

在 PHP 中,readdir函数会返回目录中的下一个文件名,如果没有更多的文件,则返回false。因此,您的代码是在循环中读取目录中的文件名,直到**readdir返回false**为止。

“!=”检查具有类型强制的不等式,而“!”检查没有类型强制的不等式,并检查类型是否相同。通常建议使用“!”进行严格比较,以避免意外的类型转换。

  • 如果是文件夹那么先走文件夹的循环遍历,到最后链接到打开按钮上,如果被触发提交GET表单,那么就会再次获取定义好的函数,并再次区分文件夹和文件,走循环

  • 遇到问题如果打开全都是文件的文件夹会报错:

    • **foreach循环尝试对一个无效的参数进行迭代。在你提供的代码中,这个警告可能是由于$list['dir']或者$list['file']**中的其中一个不是数组,或者根本不存在。

    • 解决:

      025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第10张图片

     if (isset($list['dir']) && is_array($list['dir'])): ?>
        <?php foreach ($list['dir'] as $v): ?>
            <!-- 针对目录的循环 -->
            <!-- ... 省略其他代码 ... -->
        <?php endforeach; ?>
    <?php endif; ?>
    
    <?php if (isset($list['file']) && is_array($list['file'])): ?>
        <?php foreach ($list['file'] as $v): ?>
            <!-- 针对文件的循环 -->
            <!-- ... 省略其他代码 ... -->
        <?php endforeach; ?>
    <?php endif; ?>
    

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第11张图片


// 获取路径参数,如果未提供则默认为当前目录
$path = $_GET['path'] ?? './';

// 定义获取文件列表的函数
function getlist($path) {
    // 打开目录句柄
    $hd = opendir($path);

    // 初始化文件列表数组
    $list = array();

    // 循环读取目录中的文件名
    **while (($file_name = readdir($hd)) !== false) {**
        // 排除当前目录和父目录
        if ($file_name != '.' && $file_name != '..') {
            // 构建文件完整路径
            $file_path = "$path/$file_name";

            // 获取文件类型
            $file_type = filetype($file_path);

            // 输出文件类型
            echo $file_type;

            **// 将文件信息存入列表数组
            $list[$file_type][] = array(//$file_type = dir 和 file $list['dir'] 和  $list['file']
                'file_name' => $file_name,//文件名存储键值file_name 
                'file_path' => $file_path, //文件路径存储键值file_path
                'file_size' => round(filesize($file_path) / 1024),//通过换算文件大小存储键值file_path
                'file_time' => date('Y/m/d H:i:s', filemtime($file_path)), //获取文件时间并存储键值file_path            );
        }**
    }

    // 关闭目录句柄
    closedir($hd);

    // 返回文件列表数组
    return $list;
}

// 调用函数获取文件列表
$list = getlist($path);
?>
 if (isset($list['dir']) && is_array($list['dir'])): ?>
<?php foreach ($list['dir'] as $v): ?>
    <!-- 开始循环输出文件夹目录行 -->
    <tr>
        <td><img src="./img/list.png" width="20" height="20"></td>
        <!-- 显示目录图标 -->
        <td><?php echo $v['file_name']?></td>
        <!-- 输出文件夹名 -->
        <td><?php echo $v['file_time']?></td>
        <!-- 输出文件夹修改时间 -->
        <td>-</td>
        <!-- 占位符,因为目录没有文件大小 -->
        <td><?php echo $v['file_path']?></td>
        **<!-- 输出文件夹路径 -->
        <td><a href="?path=$v['file_path']?>">打开</a></td>**
        **<!-- 在表格中创建“打开”链接,链接到目录的路径 -->**
    </tr>
    <!-- 结束循环输出目录行 -->
<?php endforeach;?>
<?php endif; ?>

<?php if (isset($list['file']) && is_array($list['file'])): ?>
<?php foreach ($list['file'] as $v): ?>
    <!-- 开始循环输出文件行 -->
    <tr>
        <td><img src="./img/file.png" width="20" height="20"></td>
        <!-- 显示文件图标 -->
        <td><?php echo $v['file_name']?></td>
        <!-- 输出文件名 -->
        <td><?php echo $v['file_time']?></td>
        <!-- 输出文件修改时间 -->
        <td><?php echo $v['file_size']?> KB</td>
        <!-- 输出文件大小 -->
        <td><?php echo $v['file_path']?></td>
        <!-- 输出文件路径 -->
        <td>
            <a href="?a=edit&path=$v['file_path']?>">编辑</a>
            <!-- 创建编辑链接 -->
            <a href="?a=down&path=$v['file_path']?>">下载</a>
            <!-- 创建下载链接 -->
            <a href="?a=del&path=$v['file_path']?>">删除</a>
            <!-- 创建删除链接 -->
        </td>
    </tr>
    <!-- 结束循环输出文件行 -->
<?php endforeach; ?>
<?php endif; ?>

#文件删除:

unlink() 文件删除函数
调用命令删除:system shell_exec exec等

  • 首先在html源码对于文件的循环处,增加删除文件的触发绑定,
<a href=?a=del&path=<?php echo $v['file_path']?>>delete</a>**生成一个"删除"链接,链接到删除文件的路径。
 当用户点击这个链接时,会向服务器发送一个请求,请求删除文件的操作。**
  • 在php源码中完成文件删除的有关代码实现
  • **isset($_GET['a'])**: 检查是否存在名为 a 的 URL 参数。
    **? $_GET['a'] : '':** 如果 a 参数存在,则将其值赋给 $action;如果不存在,则将 $action 赋为空字符串 ‘’。(三目运算符)
这行代码是用于获取 URL 参数 a 的值,表示操作类型。具体解释如下:
$action = isset($_GET['a']) ? $_GET['a'] : '';
// 接受方法 判断是怎么操作
switch ($action) {
    case 'del':
        // 如果操作类型是删除 ('del')
        unlink($file);
        // 使用 unlink 函数删除文件
        break;
}

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第12张图片

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第13张图片

#文件下载:

修改HTTP头实现文件读取解析下载:

  • header("Content-Type: application/octet-stream");: 设置响应内容的类型为二进制流(binary stream)。这告诉浏览器,服务器将发送二进制数据,通常用于文件下载。
  • header("Content-Disposition: attachment; filename=\"" . basename($file) . "\"");: 设置浏览器提示下载,同时指定下载文件的名称。basename($file) 用于获取文件名。
  • header("Content-Length: " . filesize($file));: 设置响应内容的长度为文件大小。这有助于确保浏览器知道文件的总大小。
  • readfile($file);: 读取并输出文件内容。这将把文件的内容发送到浏览器。

这样,当用户访问包含这段代码的页面时,浏览器将接收到这些头信息,然后提示用户下载名为 $file 的文件。

  • 首先在html源码对于文件的循环处,增加删除文件的触发绑定,
<a href="?a=down&path=$v['file_path']?>">下载</a>
  **生成一个“下载”链接,链接到下载文件的路径。
  当用户点击这个链接时,会向服务器发送一个请求,请求下载文件的操作。**
  • 在php源码中完成文件下载的有关代码实现
//接受方法 判断是怎么操作
switch ($action){
    case 'del':
        unlink($file);
        break;
    case 'down':
        header("Content-Type: application/octet-stream");
        // 设置响应内容的类型为二进制流
        header("Content-Disposition: attachment; filename=\"" . $file . "\"");
        // 设置浏览器提示下载,并指定下载文件的名称(使用 $file 变量)
        header("Content-Length: " . filesize($file));
        // 设置响应内容的长度为文件大小
        readfile($file);
        // 读取并输出文件内容
        break;
}
?>

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第14张图片

#文件编辑:

1、file_get_contents() 读取文件内容
2、fopen() fread() 文件打开读入

  • 首先在html源码对于文件的循环处,增加编辑文件的触发绑定
`编辑`:
   **生成一个“编辑”链接,链接到编辑文件的路径。
   当用户点击这个链接时,会向服务器发送一个请求,请求编辑文件的操作。**
  • 在php源码中完成文件下载的有关代码实现

    • 完成switch编辑文章内容展现的部分

      case 'edit':
              $content = file_get_contents($file);
              // 读取文件内容并存储在 $content 变量中
      
              echo '
      '; // 输出表单的开始标签,使用 POST 方法提交到当前页面 echo "文件名:" . $file . "
      "
      ; // 输出文件名 echo "文件内容:
      "
      ; echo '
      '
      ; // 输出包含文件内容的文本区域 echo ''; // 输出提交按钮 echo ''
      ; // 输出表单的结束标签 break;

    025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第15张图片

    • 完成触发编辑提交的按钮后,写入文件的源码

      w+fopen 函数中用于打开文件的模式之一,表示以读写方式打开文件,并将文件指针指向文件的开头。如果文件不存在,则尝试创建文件。如果文件已存在,将截断文件内容(即清空文件),然后重新写入。

      具体含义如下:

      • w: 以写入(write)方式打开文件。如果文件不存在,则尝试创建文件。如果文件已存在,则截断文件内容,即清空文件。
      • +: 以读取(read)方式打开文件。这个符号表示文件可同时进行读写操作。

      组合在一起,w+ **表示以读写方式打开文件,文件指针指向文件的开头。**在使用 w+ 模式打开文件后,你可以通过 fwrite 函数写入新的内容,同时通过 fread 函数读取文件内容。请注意,使用 w+ 模式打开文件时,原始文件内容会被清空。

    if (isset($_POST['code'])) {
        // 检查是否存在名为 'code' 的 POST 数据
    
        $f = fopen("$path/$file", 'w+');
        // 以读写方式打开文件,如果文件不存在则创建
        fwrite($f, $_POST['code']);
        // 将 POST 数据中 'code' 的内容写入文件
        fclose($f);
        // 关闭文件句柄,确保操作的文件被正确保存。
    }
    

    025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第16张图片

#安全漏洞以及防护:

显示方面漏洞

发现完成的源码程序,可以使用**../或者c:/等绕过权限访问其他目录**

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第17张图片

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第18张图片

open_basedir:PHP.INI中的设置用来控制脚本程序访问目录
ini_set(‘open_basedir’,__DIR__); 设置配置文件中,只能访问本目录

  • ini_set('open_basedir', __DIR__);: 使用 ini_set 函数设置 PHP 配置项。
  • 'open_basedir': 是要设置的配置项,它规定了允许访问的文件和目录的根路径。
  • __DIR__: 这是一个 PHP 预定义常量,表示当前脚本所在的目录。在这里,open_basedir 被设置为当前脚本所在的目录,即只允许 PHP 脚本访问当前目录及其子目录下的文件和目录。

通过设置 open_basedir,你可以限制 PHP 脚本只能访问指定路径下的文件和目录,以增强安全性。在这个例子中,open_basedir 被设置为当前脚本所在的目录,这意味着 PHP 脚本只能访问当前目录及其子目录下的文件和目录,而不能越过这个限制。

  • 解决方式:
    • 在代码中设置ini_set(‘open_basedir’,__DIR__);、
    • open_basedir:PHP.INI中的设置用来控制脚本程序访问目录

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第19张图片

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第20张图片

下载方面漏洞

发现完成的源码程序,可以使用**../或者c:/等绕过权限下载自己想要下载的文件**

http://localhost:63342/dome01/filemanage.php?a=down&path=.//filemanage.php

更改为http://localhost:63342/dome01/filemanage.php?a=down&path=c:\list.png

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第21张图片

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第22张图片

删除方面漏洞

  1. 还是可以替换网址的路由路径,进行删除自己任意想删除的文件

    localhost:63342/dome01/filemanage.php?a=del&path=.//1.txt替换为c:\list.png

    025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第23张图片

  2. 删除文件还可以使用调用系统cmd来实现(但是容易将cmd的权限泄露出走)

    case 'del':
            //unlink($file);
            $cmd="del $file";
            system($cmd);
            echo $cmd;
            break;
    

    Untitled

    025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第24张图片

025-安全开发-PHP应用&文件管理&包含&写入&删除&下载&上传&遍历&安全_第25张图片

你可能感兴趣的:(安全,php,android)