Day25 XD安全笔记 安全开发-PHP 应用&文件管理模块&包含&上传&遍历&写入&删除&下载&安全

从开发视角看安全,哪怕只是txt文件,内容为php语句,只要被包含就能解析!如果将包含恶意语句的文件后缀改为可以上传的文件,只要能上传上去知道存放的位置和可控的包含漏洞就能执行恶意代码。没有限制跨目录访问且有可控的路径传参就可能存在目录遍历漏洞,导致敏感文件被访问。再结合下载和删除功能就扩大成任意下载删除漏洞。如果这些功能是调用服务器的命令行进行操作,那就可能存在RCE漏洞。这节课根据 URL 参数判断操作类型,对指定文件进行删除、下载、编辑,或列出目录内容。

成品展示

代码实现以及注释:

1.filemanage.php
复制代码
<?php
ini_set('open_basedir',__DIR__);//设置php配置项,open_basedir设置为当前目录__DIR__,防止跨目录访问
$action = isset($_GET['a'])?$_GET['a']:'';// isset():判断变量是否已设置且不为 null,接收a参数值
$path = isset($_GET['path'])?$_GET['path']:'.';

if(is_file($path))// is_file():判断给定路径是否为一个普通文件。
{
    $file = basename($path);// basename():返回路径中的文件名部分,例如 /var/www/a.txt 返回 a.txt。
    //获得路径
    $path = dirname($path);// dirname():返回路径中的目录部分。例如 /var/www/a.txt 返回 /var/www。
}

elseif(!is_dir($path))// !is_dir():判断给定路径是否不是目录
{
    echo '我只会吃瓜!';
}
function getlist($path){
    $hd=opendir($path);// opendir():打开目录句柄,成功返回目录句柄资源,失败返回 false。句柄是程序操作某个系统资源时使用的"引用"或"凭证",不是资源本身。通过句柄,程序才能读、写、关闭对应资源。
    while(($file_name=readdir($hd))!=false){ // readdir():从目录句柄中读取下一个条目。
        if($file_name != '.' && $file_name != '..'){// 跳过当前目录 "." 和上级目录 ".."。
            $file_path = "$path/$file_name";// 拼接完整路径。
            $file_type = filetype($file_path);// filetype():返回文件类型,例如 file、dir、link 等
        }
/*
 * 举例说明下面数组的存储方式
假设当前目录 ./ 下有:

文件夹 css

文件 index.php

文件 logo.png

while 循环会一个一个读到它们。

1. 读到 css 文件夹
$file_name = 'css'

$file_path = './css'

$file_type = 'dir'

执行:

php
$list['dir'][] = array(
    'dir' => [],
    'file' => [],
    'file_name' => 'css',
    'file_path' => './css',
    'file_size' => round(filesize('./css')/1024),
    'file_time' => date('Y/m/d H:i:s', filemtime('./css')),
);
此时 $list['dir'] 里有了第 0 个元素:

php
$list['dir'][0] = [
    'dir' => [],
    'file' => [],
    'file_name' => 'css',
    'file_path' => './css',
    'file_size' => 4,
    'file_time' => '2026/10/11 12:00:00'
];
2. 读到 index.php 文件
$file_name = 'index.php'

$file_path = './index.php'

$file_type = 'file'

执行:

php
$list['file'][] = array(
    'dir' => [],
    'file' => [],
    'file_name' => 'index.php',
    'file_path' => './index.php',
    'file_size' => round(filesize('./index.php')/1024),
    'file_time' => date('Y/m/d H:i:s', filemtime('./index.php')),
);
此时 $list['file'] 里有了第 0 个元素:

php
$list['file'][0] = [
    'dir' => [],
    'file' => [],
    'file_name' => 'index.php',
    'file_path' => './index.php',
    'file_size' => 2,
    'file_time' => '2026/10/11 12:00:00'
];
3. 读到 logo.png 文件
$file_name = 'logo.png'

$file_path = './logo.png'

$file_type = 'file'

执行:

php
$list['file'][] = array(...);
因为 $list['file'] 已经有一个元素了,所以新元素会被放在下标 1:

php
$list['file'][1] = [
    'dir' => [],
    'file' => [],
    'file_name' => 'logo.png',
    'file_path' => './logo.png',
    'file_size' => 10,
    'file_time' => '2026/10/11 12:00:00'
];
四、最终 $list 长什么样?
把所有元素拼起来,$list 就是这样的二维数组:

php
$list = [
    'dir' => [
        0 => [
            'dir' => [],
            'file' => [],
            'file_name' => 'css',
            'file_path' => './css',
            'file_size' => 4,
            'file_time' => '2026/10/11 12:00:00'
        ]
    ],
    'file' => [
        0 => [
            'dir' => [],
            'file' => [],
            'file_name' => 'index.php',
            'file_path' => './index.php',
            'file_size' => 2,
            'file_time' => '2026/10/11 12:00:00'
        ],
        1 => [
            'dir' => [],
            'file' => [],
            'file_name' => 'logo.png',
            'file_path' => './logo.png',
            'file_size' => 10,
            'file_time' => '2026/10/11 12:00:00'
        ]
    ]
];
五、怎么访问里面的数据?
想拿第一个目录的名字:
$list['dir'][0]['file_name'] → 'css'

想拿第一个文件的路径:
$list['file'][0]['file_path'] → './index.php'

想拿第二个文件的大小:
$list['file'][1]['file_size'] → 10
*/

        $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);// closedir():关闭目录句柄,释放资源。
    return $list;
}

switch ($action){
    case 'del':
        unlink($file);
//        $cmd="del $file";
        header('Location: ?path=' . dirname($path));//避免访问删除文件路径报错,重定向到用dirname来提取链接的目录路径
        exit;

        //system($cmd);//如果服务器操作系统是windows,删除文件调用系统的命令,如果这个值能被用户控制,则会出现RCE命令漏洞,拼接上其他操作命令
        //echo $cmd;
    case 'down':
        // header():设置 HTTP 响应头
        header("Content-Type: application/octet-stream");// Content-Type: application/octet-stream 表示二进制流,浏览器通常以下载方式处理。
        header("Content-Disposition: attachment; filename=\"" . $file . "\"");// Content-Disposition: attachment 表示作为附件下载。filename 指定下载时显示的文件名。
        header("Content-Length: " . filesize($file));// Content-Length:告诉浏览器响应正文的字节长度。filesize():获取文件大小。
        readfile($file);// readfile():读取文件并直接输出到输出缓冲区。
        break;
    case 'edit':
        $content=file_get_contents($file);// file_get_contents():将整个文件读入一个字符串。
        echo '<form name="form1" method="post" action="">';// 输出一个表单,用于编辑文件内容。
        echo "文件名:".$file."<br>";
        echo "文件内容:<br>";
        echo '<textarea name="code" style="resize:none;" rows="100" cols="100"">'.$content.'</textarea><br>';// textarea 中显示原文件内容。
        echo '<input type="submit" name="submit" id="submit" value="提交">';// name="code" 用于 POST 提交。
        echo '</form>';
        break;
}

//检测编辑后提交的事件 进入文件重新写入
if(isset($_POST['code'])){
    $f=fopen("$path/$file",'w+');// fopen():打开文件或 URL。"w+" 表示以读写方式打开,清空原文件内容;如果文件不存在则尝试创建
    fwrite($f,$_POST['code']);// fwrite():将字符串写入已打开的文件。
    fclose($f);
}

$list=getlist($path);
?>

<!-- 下面是 HTML 表格,用于展示目录内容。 -->
<table width="100%" style="font-size: 10px;text-align: center;">
    <tr>
        <th>图标</th>
        <th>名称</th>
        <th>日期</th>
        <th>大小</th>
        <th>路径</th>
        <th>操作</th>
    </tr>

    <!-- foreach:遍历数组。这里遍历 $list['dir'],即所有子目录。 -->
    <!-- $v 表示当前遍历到的目录元素。 -->
    <?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>-</td>
            <td><?php echo $v['file_time']?></td>
            <td><?php echo $v['file_path']?></td>
            <td><a href="?path=<?php echo $v['file_path']?>">打开</a></td>
        </tr>
    <?php endforeach;?>

    <!-- 遍历 $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']?></td>
        <td><?php echo $v['file_path']?></td>
        <td>
<!--            点击链接以get方式传入a参数,由switch判断不同的值对应不同的操作-->
            <a href="?a=edit&path=<?php echo $v['file_path']?>">编辑</a>
            <a href="?a=down&path=<?php echo $v['file_path']?>">下载</a>
            <a href="?a=del&path=<?php echo $v['file_path']?>">删除</a>
        </td>
        <?php endforeach;?>
    </tr>



</table>
2.edit
php 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>内容更改</title>
</head>
<body>
<form name="form1" method="post" action="filemanage.php">
  文件名:<?php echo $file?><br>
  文件内容:
  <br><br>
  <input type="submit" name="submit" id="submit" value="提交">
</form>
</body>
</html>
相关推荐
Awh-1 小时前
ARM 裸机开发 DAY10 学习笔记
arm开发·笔记·学习
He BianGu1 小时前
【笔记】WPF中CachingHint的功能
笔记·wpf
itmigrate1 小时前
PHP 导出大 CSV 时内存溢出的一次排查
android·开发语言·php
问心无愧05132 小时前
[ACTF2020 新生赛]Exec
运维·服务器·笔记
白帽攻防录2 小时前
SRC 挖洞:QUIC 协议栈双 RCE,HTTP/3 的新攻击面怎么挖
网络·网络协议·http·网络安全·http/3
墨云安全2 小时前
AI 漏洞挖掘平台怎么评估?2026 年技术架构与选型要点
网络·人工智能·网络安全·架构·渗透测试
云半S一2 小时前
Agent 学习总结一
经验分享·笔记·学习·agent
跨知易知识产权3 小时前
美国发明专利和外观专利的保护期限分别是多久,外观专利是否需要缴纳维持费?
笔记
八月的雨季 最後的冰吻3 小时前
C++ 面试要点笔记(续)
c++·笔记·面试