☰
Day24 XD安全学习笔记--PHP 应用文件管理模块显示上传黑白名单类型过滤访问控制
2026/10/11 8:48:14 网站建设 项目流程

目录

一.文件上传

1.代码实现

(1)upload.html

(2)upload.php

2.遇到的问题及解决

二.文件管理

1.代码实现

2.遇到的问题及解决


实现简单的上传文件后缀黑名单过滤、上传文件后缀白名单过滤和文件类型白名单过滤(MIME)。从开发视角来看可能存在的漏洞绕过,黑名单不包含,文件类型改包。最安全的是多种过滤一起用,增加攻击者的门槛。代码附详细的注释。

B站免费课程链接 :

www.bilibili.com/video/BV12om2YTEgW/?spm_id_from=333.788.videopod.episodes&vd_source=7d8464f1ea919ffdc1761c211c6d1c4e&p=25

一.文件上传

1.代码实现

(1)upload.html
<!--用deepseek写一个纯html精致的文件上传表单--> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>文件上传</title> <style> * { box-sizing: border-box; } html, body { height: 100%; } body { margin: 0; padding: 24px; display: flex; align-items: center; justify-content: center; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif; background: radial-gradient(circle at 15% 15%, rgba(255, 255, 255, .18), transparent 45%), radial-gradient(circle at 85% 85%, rgba(255, 255, 255, .12), transparent 45%), linear-gradient(135deg, #667eea 0%, #764ba2 100%); } /* ============ 卡片 ============ */ .card { width: 100%; max-width: 440px; padding: 38px 34px 34px; background: #ffffff; border-radius: 22px; box-shadow: 0 30px 60px -18px rgba(23, 20, 70, .45), 0 0 0 1px rgba(255, 255, 255, .6) inset; animation: rise .5s ease both; } @keyframes rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: translateY(0); } } /* ============ 头部 ============ */ .head { text-align: center; } .badge { width: 60px; height: 60px; margin: 0 auto; display: flex; align-items: center; justify-content: center; border-radius: 18px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); box-shadow: 0 12px 24px -8px rgba(102, 126, 234, .85); } h1 { margin: 20px 0 8px; font-size: 22px; font-weight: 700; color: #1f2937; letter-spacing: .5px; } .subtitle { margin: 0; font-size: 13.5px; line-height: 1.7; color: #6b7280; } /* ============ 文件选择框 ============ */ .file-input { display: block; width: 100%; margin-top: 28px; padding: 10px; font-family: inherit; font-size: 13.5px; color: #6b7280; background: #f8f9ff; border: 2px dashed #cdd5f5; border-radius: 14px; cursor: pointer; transition: border-color .25s, background .25s, box-shadow .25s; } .file-input:hover { border-color: #8b95f0; background: #f2f4ff; } .file-input:focus { outline: none; border-color: #667eea; background: #f2f4ff; box-shadow: 0 0 0 4px rgba(102, 126, 234, .16); } /* 美化"选择文件"按钮(标准写法) */ .file-input::file-selector-button { margin-right: 14px; padding: 10px 18px; border: 0; border-radius: 10px; font-family: inherit; font-size: 13.5px; font-weight: 600; color: #ffffff; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); box-shadow: 0 8px 16px -8px rgba(102, 126, 234, .95); cursor: pointer; transition: filter .2s, transform .15s; } .file-input::file-selector-button:hover { filter: brightness(1.08); } .file-input::file-selector-button:active { transform: scale(.97); } /* 兼容旧版 WebKit 内核 */ .file-input::-webkit-file-upload-button { margin-right: 14px; padding: 10px 18px; border: 0; border-radius: 10px; font-family: inherit; font-size: 13.5px; font-weight: 600; color: #ffffff; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); cursor: pointer; } /* ============ 提示文字 ============ */ .tip { margin: 14px 0 0; text-align: center; font-size: 12.5px; color: #9ca3af; line-height: 1.6; } /* ============ 提交按钮 ============ */ .btn { display: block; width: 100%; margin-top: 24px; padding: 15px; border: 0; border-radius: 13px; font-family: inherit; font-size: 16px; font-weight: 600; letter-spacing: 1px; color: #ffffff; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); box-shadow: 0 14px 26px -10px rgba(102, 126, 234, .95); cursor: pointer; transition: transform .2s, box-shadow .2s, filter .2s; } .btn:hover { transform: translateY(-2px); filter: brightness(1.06); box-shadow: 0 18px 30px -12px rgba(102, 126, 234, 1); } .btn:active { transform: translateY(0); box-shadow: 0 10px 18px -10px rgba(102, 126, 234, .95); } .btn:focus-visible { outline: 3px solid rgba(102, 126, 234, .45); outline-offset: 3px; } /* ============ 移动端适配 ============ */ @media (max-width: 420px) { .card { padding: 30px 22px 26px; border-radius: 18px; } h1 { font-size: 20px; } } </style> </head> <body> <form class="card" action="/upload.php" method="post" enctype="multipart/form-data"> <!-- 头部图标与标题 --> <div class="head"> <div class="badge"> <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="17 8 12 3 7 8"/> <line x1="12" y1="3" x2="12" y2="15"/> </svg> </div> <h1>上传文件</h1> <p class="subtitle">选择需要上传的文件,然后点击下方按钮提交</p> </div> <!-- 文件选择 --> <input class="file-input" type="file" name="file" id="file" multiple required> <p class="tip">支持 PDF / Word / Excel / 图片等格式,单个文件不超过 10MB</p> <!-- 提交 --> <button class="btn" type="submit">开始上传</button> </form> </body> </html>

(2)upload.php
<?php $name=$_FILES['file']['name'];//第一个参数是表单提交文件的name值,也就是file。获取表单提交文件的名字 $type=$_FILES['file']['type'];//获取表单提交文件的类型 $size=$_FILES['file']['size'];//获取表单提交文件的大小 $tmp_name=$_FILES['file']['tmp_name'];// 获取服务器上的临时文件路径 $error=$_FILES['file']['error'];// 错误码 //echo $name."<br>"; //echo $type."<br>"; //echo $size."<br>"; //echo $tmp_name."<br>"; //echo $error."<br>"; //if(move_uploaded_file($tmp_name,'upload/'.$name)){ ////move_uploaded_file函数是把临时文件移动到指定目录 // echo "文件上传成功!"; //} //文件后缀黑名单过滤(特定环境可以用类似php5来绕过,但是也执行) //$black_ext=array('php','asp','jsp','aspx');//将禁止上传文件的后缀写入数组 ////xxx.jpg xxx.jpg //$fenge=explode('.',$name);//将文件名用.分隔,然后变成数组。比如上传的文件名为xxx.jpg,分隔之后变成数组存储。等价于$fenge=['xxx','jpg'] //$exts=end($fenge);//提取数组$fenge中最后一个元素传给变量$exts //if(in_array($exts,$black_ext)){ ////in_array函数用来判断黑名单数组$black_ext里面包不包含上传文件后缀$exts // echo '非法后缀文件'.$exts; //}else{ // move_uploaded_file($tmp_name,'',$name); // echo '<script>alert("上传成功")</script>'; //} //文件后缀白名单过滤(和上面的黑名单差不多,只是判断条件不一样) $allow_ext=array('png','jpg','gif','jpeg'); //xxx.jpg xxx.png $fenge=explode('.',$name); $exts=end($fenge); if(!in_array($exts,$allow_ext)){ echo '非法后缀文件'.$exts; }else{ move_uploaded_file($tmp_name,'upload/'.$name); echo '<script>alert("上传成功")</script>'; } //MIME白名单过滤(和上面的文件后缀白名单过滤差不多,可以通过抓包将php后缀的文件类型改成image/png的) $allow_type=array('image/png','image/jpg','image/jpeg','image/gif'); if(!in_array($type,$allow_type)){ echo '非法文件'.$type; }else{ move_uploaded_file($tmp_name,'upload/'.$name); echo '<script>alert("上传成功")</script>'; }

2.遇到的问题及解决

调试访问上传的文件报错

可以用小皮的环境访问

改文件类型绕过MIME

将application/octet-stream改为image/png绕过

文件后缀黑名单绕过,test.php5解析<?php phpinfo();?>

二.文件管理

1.代码实现

file-manage.php

<?php $dir=$_GET['path'] ?? './'; //获取路径,默认./ //$dir='./'; function show_file($dir){ $d=opendir($dir);//opendir() 函数用于打开指定的目录,返回句柄,用来读取目录中的文件和子目录 while(($file=readdir($d))!=false){ //readdir() 函数用于从打开的目录句柄中读取目录中的文件和子目录 echo "<br>"; if(is_dir($file)){ //is_dir() 函数用于检查指定的路径是否是一个目录 echo '文件夹:'."<a href='?path=$file'>$file</a><br>"; //通过url地址栏加上path参数,好让$dir接收 }else{ echo '文件:'.$file; } } } $black_filepath=array('../','..\\');//可以用./或者././等绕过,太灵活了 if(in_array($dir,$black_filepath)){ echo '<script>alert("禁止跨目录访问")</script>'; }else{ show_file($dir);//将函数filelist改为show_file() }

2.遇到的问题及解决

获取路径错误,$dir=$_GET['path'] ?? './';的空字符加入./

怎么都是1?

遍历目录和文件while(($file=readdir($d))!=false)里面的$file=readdir($d)要括起来,不然按照运算符优先级先执行readdir($d)!=false,运算的结果为布尔赋值给$file了。

为什么filelist函数报错?因为filelist是小迪之前就写好的代码文件有这个函数,用show_file替换就好了。

可以找到小皮的php.ini文件来限制目录的访问

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询