安全开发-PHP-文件管理
token模块核心
注意:
1.浏览器的插件可能会影响token的使用,例如插件在你发出请求后重新刷新请求,导致数据包永远是不唯一的。
2.token使用的是session的会话维持
3.token是唯一的。
我的个人理解:
1.session的会话维持的特性,加入session的值在一定时间内是恒定不变的
2.利用生成算法等方式,创建一个唯一的token,并且传递给表单,让表单隐性提交过来
3.当表单提交的token值与服务器对不上时(!==),服务器更新token值。
4.当表单正确时,将token再次更新,确保数据包的唯一性。
作用:确保数据包的唯一性,相当于强制用户必须使用当前页面登录。
(反正每一次访问都会刷新token,并且想要破解token几乎不可能,除非程序员的设计有问题等。)
<?php session_start();
  $token = $_POST['token'] ?? '';
 
  if ($token !== $_SESSION['token']) {          header('HTTP/1.1 403 Forbidden');     $_SESSION['token'] = bin2hex(random_bytes(16));     echo 'Access denied';     exit; }else{     $_SESSION['token'] = bin2hex(random_bytes(16));     if($_POST['username']=='admin' && $_POST['password']=='123456'){         echo '登录成功!';         echo '你是管理员可以访问文件管理页面!';     }else{         echo '登录失败!';     } }
   | 
 
PHP文件管理
目的:为后期代码审计做铺垫。
界面设计
前端部分
<!DOCTYPE html> <html>
  <head>     <meta charset="utf-8">     <title>文件列表</title>     <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">     <style type="text/css">         ul {             list-style: none;             padding: 0;             margin: 0;         }
          li {             margin-bottom: 10px;         }
          i {             margin-right: 10px;         }
          a {             text-decoration: none;             color: #333;         }     </style> </head>
  <body>     <h1>当前目录下的文件列表</h1>     <ul></ul> </body> </html>
   | 
 
后端部分
<?php
  $dir = $_GET['path'] ?? './';
 
 
 
 
 
 
  function filelist($dir) {     if ($dh = opendir($dir)) {                  while (($file = readdir($dh)) !== false) {                          if (is_dir($file)) {                 echo "<li><i class='fa fa-folder'></i> <a href='?path=$file'>" . $file . '</a></li>';             } else {                 echo '<li><i class="fa fa-file"></i> <a href="#">' . $file . '</a></li>';             }         }     } }
  filelist($dir);
  function del($file) {     if (!is_dir($file)) {         unlink($file);         echo "<script>alert('删除成功')</script>";     } }
  if (isset($_GET['del'])) {     del($_GET['del']); }
 
  function down($filepath) {     $fileName = basename($filepath);     header("Content-Type: application/octet-stream");     header("Content-Disposition: attachment; filename=\"" . $fileName . "\"");     header("Content-Length: " . filesize($filepath));     readfile($filepath); }
  if (isset($_GET['down'])) {     down($_GET['down']); } ?>
   | 
 
漏洞
php.ini文件: 如果不慎关闭这个设置,会导致全局目录遍历漏洞出现。导致本地的所有文件都会被攻击者浏览和下载。 关闭下面设置:形成文件遍历漏洞 open_basedir
  防止被翻目录的方法:使用代码内语法限制,或者打开上面的open_basedir对访问目录进行限制
   | 
 
黑名单+白名单+文件类型限制
黑名单白名单
黑名单:写好不能上传的文件类型。
白名单:指定好能上传的文件类型(推荐)。
  $black_ext = array('php','aps','jsp','aspx','js');
 
  $chaifeng = explode('.',$_POST['file']['name']);
  $exts = end($chaifeng);
  if(is_array($exts,$black_ext)){     echo '非法后缀文件'; } else {          move_uploaded_file($tmp_name,'upload/'.$name); }
 
  $allow_ext = array('php','aps','jsp','aspx','js');
 
  $chaifeng = explode('.',$_POST['file']['name']);
  $exts = end($chaifeng);
  if(is_array($exts,$allow_ext)){     move_uploaded_file($tmp_name,'upload/'.$name);     } else {     echo '非法后缀文件'; }
 
 
  | 
 
文件类型限制
下面数数据包
POST /PHPStorm/demo02-file/upload.php HTTP/1.1 Host: localhost:63342 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
   | 
 
经过观察,可以发现Content-Type指定上传文件的文件类型是image/jpeg。
安全风险:改下文件类型的数据包就可以了。需要验证逻辑严谨些才能显得安全些。
$type = $_POST['file']['type'];
  $allow_type=array('image/png','image/jpg','image/jpeg','image/gif'); if(in_array($type,$allow_type)){     move_uploaded_file($tmp_name,'upload/'.$name);     echo '<script>alert("上传成功")</script>';     }else{     echo '非法文件类型';
  }
   | 
 
文件包含:
include() 在错误发生后脚本继续执行     功能:'包含'哪个文件就执行那个文件             把任意一个文件调用过来执行。           => 任意脚本执行漏洞:如下           include($_GET['page']); require() 在错误发生后脚本停止执行 include_once() 如果已经包含,则不再执行 require_once() 如果已经包含,则不再执行
   | 
 
文件删除:
unlink() 文件删除函数
调用命令删除:system shell_exec exec等
php调用WindowsCMD删除文件 system('del 1.txt'); 安全问题:任意命令执行漏洞。
   | 
 
文件下载:
修改HTTP头实现文件读取解析下载:
header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename=""); header("Content-Length: " . filesize($file)); readfile($file);
  | 
 
文件编辑:
1、file_get_contents() 读取文件内容
2、fopen() fread() 文件打开读入
文件编辑下载删除实现
switch ($action){     case 'del':       echo "<script>console.log('$fname')</script>";       unlink("$path/$fname");       header("Location: http://127.0.0.1:8028/all_file_read-x.php?path=$path");       echo "<script>window.location.href='http://127.0.0.1:8028/all_file_read-x.php?path=$path'</script>";       break;     case 'down':       header("Content-Type: application/octet-stream");       header("Content-Disposition: attachment;filename=\"".$fname."\"");       header("Content-Length: ".filesize($fname));       break;     case 'edit':       $content = file_get_contents("$path/$fname");       echo '<form name="form1" method="post" action="all_file_read-x.php">';       echo "文件名:".$fname."<br>";       echo "文件内容:<br>";       echo '<textarea name="codes" style="resize:none;" rows="100" cols="100"">'.$content.'</textarea><br>';       echo '<input type="submit" name="submit" id="submit" value="提交">';       echo "<input type='hidden' value='$path/$fname' name='pf'>";       echo '</form>';       break;   }   if (isset($_POST['submit'])){     if(isset($_POST['codes'])){       file_put_contents($_POST['pf'],$_POST['codes']);     }   }
  |