原生开发
课堂完结后欲复习巩固也方便后续-重游-故写此篇
从实现功能过渡到涉及的相关知识点
留言板功能
简单流程:浏览器访问 -> 后端php代码接收、处理请求,并连接数据库查询 -> 数据库返回数据给php -> 后端响应数据给浏览器
1、 php文件可以兼容http和php
2、 浏览器正确显示php文件:由-中间件-解析执行php并把结果返回给浏览器显示
3、 浏览器“看不到”php代码(后端语言)
连接数据库
一、 另建一个 config.php 文件作为数据库连接的全局配置文件,只在需要的地方导入就好了。
1 2 3 4
| require 'config.php';
include 'config.php';
|
二、 config.php 代码如下
1 2 3 4 5 6 7 8 9 10
| <!-- 数据库连接的全局配置文件 --> <?php
$dbhost="localhost:3306"; $dbuser="db_name"; $dbpwd="db_pwd"; $dbname="tb_name";
$mysqli_connect=mysqli_connect($dbhost,$dbuser,$dbpwd,$dbname); ?>
|
三、 数据库基本知识复习
1、 查询
1
| select * from user where id=1;
|
2、 删除
1
| delete from user where id>10;
|
3、 插入
1 2 3 4
| //全插入 insert into user values(1,"Nailu",22),(2,"张三",39) //插部分 insert into user(id,username) value(3,"李四");
|
4、 更新
1
| update user set id=250,username="test_name" where id=3;
|
后端php
一、 接收浏览器数据
1、 php的-超级全局变量-所有作用域都可访问
2、 一般利用超级全局变量 $_POST("v_name")
和$_GET("v_name")
来接收数据
3、 还有一些超级全局变量
$GLOBALS(变量)
$_SERVER(请求的信息)
$_REQUEST(响应的信息)
$_FILES(文件)
$_ENV(服务器端环境) -> $_ENV[‘pwd’]
$_COOKIE(鉴权)
$_SESSION(鉴权)
二、 处理并显示给浏览器
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| <?php
require 'config.php';
function show_p($i,$del){ echo "$del"."<br>";
$yuju="select * from gbook;"; ; if($res=mysqli_query($i,$yuju)){ while($array=mysqli_fetch_row($res)){ echo "<hr>"; echo "用户名:"."$array[1]"."<br>"; echo "内容"."$array[2]"."<br>"; echo "评论时间:"."$array[3]"."<br>"; echo "ip地址:"."$array[4]"."<br>"; if ($del=="del") { echo "<a href='./del.php?id=$array[0]'>删除</a>"."<br>"; echo "$array[0]"; } }
}else{ echo '连接数据库显示留言失败!'; } }
function add_p($i){ $name=$_POST['uname']; $contents=$_POST['content']; $ip=$_SERVER['REMOTE_ADDR']; $t =date('Y-m-d H:i:s', intval($_SERVER['REQUEST_TIME'])); if (!empty($name)&&!empty($contents)) { $sql="insert into gbook value('0','$name','$contents','$t','$ip')"; if(mysqli_query($i,$sql)){ echo "<script>alert('评论成功!')</script>"; }else{ echo "<script>alert('评论失败\\\x7e')</script>"; }
} }
show_p($mysqli_connect,'x'); add_p($mysqli_connect);
?>
|
安全问题
1、 水平越权漏洞
当不是每个网页每次请求都验证 Cookie 或者 Session 或者 token 而有依据参数判断的话,就好发生水平越权,用户可以暴力破解出管理员的参数值从而控制网页。
2、 XSS(跨站脚本攻击)
观察,是留言板,那么用户在评论的内容里输入php代码是都会被执行呢?关键在于是否过滤,还是直接写入数据库。可以用第三方组件,可以自己写code,也可以是混合。
3、 第三方富文本编辑器组件的漏洞
比如留言板需要图片呐,文件呐等更丰富的输入形式,那么就可以用上第三方的留言框,但是如果下载的版本有历史漏洞的话那么就会连带着网站也有了漏洞
后台验证功能【以 token 为例】
就如上面-连接数据库-里的-安全问题-一样,如果没有每个网站每次请求都鉴权那么就会-水平越权-。
那么三种方式哪种安全呢,各自有啥特点以及适合哪些领域呢?
1、 鉴权技术
Cookie:放在浏览器里,每次请求附带(可加密)
Session:放在服务端,每次请求附带 sessId 在cookie里
Token:最新也是最安全的技术,每次请求都有唯一 token,通常的话是放在session里面,防爆破,防 csrf(跨站请求伪造)
2、 Session 小部分
session_start();
是php里启动sesson服务的语句,只要你请求里cookie中附带SESSID那么就可以“联系”上 id 唯一的sesison文件并可以读取内容。所以不需要用户开发服务端的时候去写code。
Session 文件的默认保存路径在 php.ini 文件里的 session.save_path 上改。
3、 token 部分
为了让每次表单提交都是唯一的,里面得添加每次刷新变化的 token 值(服务端也同步刷新)。如下。
1
| <input type="hidden" name="privatetoken" value="<?php echo $token;?>" />
|
4、 @作用
@的作用是抑制出错时信息的显示,比如
1 2 3
| $file = @file('non_existent_file'); $name = "keyword at in PHP"; echo $name;
|
没有@结果是
/a_1.png)
有@:
/a_2.png)
也没特别的流程,直接上代码:
首先是登录页面的代码,一个简单的表单提交给 index-t.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| <?php @session_start();
$token=md5(getrandcode()); $_SESSION['token'] = $token; function getrandcode(){ $str =array(0,1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f','g','h'); $res=''; for($i=0;$i<4;$i++){ $rand=mt_rand(1,17); $res .=$str[$rand]; } return $res; } ?>
</html> <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>登录页面</title> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; font-family: sans-serif; } .login-form { padding: 20px; border: 1px solid border-radius: 8px; width: 300px; } .error-message { color: red; display: none; } </style> </head> <body> <form class="login-form" method="post" action="./index-t.php"> <h3>用户登录</h3> <div> <label for="username">用户名:</label> <input type="text" id="username" name="username" required> </div> <input type="hidden" name="privatetoken" value="<?php echo $token;?>" /><br> <div> <label for="password">密码:</label> <input type="password" id="password" name="password" required> </div> <input type="submit" value="登录"> </form> </body> </html>
|
然后是首页的代码,由于是简单模拟,所以当 token 正确后直接把 token 删了,没有写刷新后的 token 赋值之类的\x7e。
code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?php @session_start(); if($_POST) { echo "1".$_SESSION['token']."<br>"; echo "2".$_POST['privatetoken']."<br>"; if ($_POST['privatetoken'] == $_SESSION['token']) { unset($_SESSION['token']); echo "<h1>后台首页</h1>";
echo "欢迎".$_SESSION["username"].",这里是用户页面";
} else { echo 'novalite'; } }else{ header("Location: http://localhost/class_1/admin/login-t.php"); exit(); }
?>
|
安全问题
1、 cookie窃取 和 Session劫持(会话劫持)
当 kacker 通过莫种方式拿到了 cookie 或者 SESSID 的话那就可以模拟该用户发送请求。
文件管理功能
php的文件操作无非文件读取、接收、修改、保存一类。跟着课堂写了一个浏览器显示操作的文件编辑器。能够下载、上传、修改、删除文件。
/a_3.png)
1、 文件管理系统的可访问目录修改
可以在 php.ini 文件里修改 open_basedir,一个点”.”为当前目录;也可以在代码里修改ini_set('open_basedir','.');
。
2、 文件下载就是请求包里面的参数不同
//设置下载请求
header(“Content-type:application/octet-stream”);
header(“Accept-Length:”.filesize($file_path));
header(“Content-Disposition: attachment; filename=”.$file_path);
3、 读取 form 表单传送过来的文件
需要用到超级全局变量 $_FILES,实例代码如下(不完整
1 2 3 4 5 6 7 8 9 10 11 12
| <form action="upload.php" method="post" enctype="multipart/form-data"> <input type="file" name="fileToUpload" id="file"> <button type="submit">上传文件</button> </form>
$name=$_FILES['fileToUpload']['name']; $type=$_FILES['fileToUpload']['type']; $size=$_FILES['fileToUpload']['size']; $tmp_name=$_FILES['fileToUpload']['tmp_name']; $error=$_FILES['fileToUpload']['error'];
|
文件管理系统
由于并不复杂,就集合与一个php文件中,有些简陋并没有考虑到只包含文件或者只包含文件夹的情况。就只是遍历显示\x7e
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
| <?php
$dir=$_GET['path'] ?? ".";
$file_list;
function show_file($path){ global $file_list; $f=opendir($path); while(($s=readdir($f))!=false){ if($s!="." && $s!=".."){ $file_path="$path/$s"; if(file_exists($file_path)){ $file_type=filetype($file_path); }else{ echo "文件路径--".$file_path."--不存在"; }
} $file_list[$file_type][]= array( 'file_name' => $s, 'file_size' => round(filesize($file_path)/1024), 'file_time' => date('Y-m-d H:i:s',filemtime($file_path)), 'file_path' => $file_path );
} }
ini_set('open_basedir','.');
if(is_dir($dir)){ show_file($dir); }else{ dir('路径请求错误!不是文件夹路径!'); }
function delete($file_path){ unlink($file_path); }
function download($file_path){ header("Content-type:application/octet-stream"); header("Accept-Length:".filesize($file_path)); header("Content-Disposition: attachment; filename=".$file_path); }
function edit_file_show($file_path){ $content=file_get_contents($file_path); echo "<form name='form1' method='post' action=''>"; echo "文件名:".$file."<br>"; echo "文件内容:<br>"; echo "<textarea name='eidt_code' style='resize:none 'rows='50' cols='50'>".$content."</textarea><br><br>"; echo "<input type='submit' name='submit' id='submit' value='提交'><br>"; echo "</form>";
}
function edit_file_action($file_path,$content){ echo "file::".$content; $f=fopen($file_path,'w+'); fwrite($f,$content); fclose($f); }
$action=$_GET['action'] ?? '';
$file=$_GET['file_path'] ?? '';
switch ($action) { case 'del': delete($file); break; case 'down': download($file); break; case 'edit': edit_file_show($file); break; default: break; }
if(isset($_POST['eidt_code'])){edit_file_action($file,$_POST['eidt_code']);}
?>
<!--
====== ======前端的显示部分 ======
-->
<!DOCTYPE html> <html lang="zh-CN"> <head></head> <body> <table> <thead> <tr> <th>类型</th> <th>名字</th> <th>日期</th> <th>路径</th> <th>大小</th> <th>操作</th> </tr> </thead> <!-- 循环文件夹 --> <?php foreach ($file_list['dir'] as $v): ?> <tbody> <tr> <td><img src="./img/dir.webp" height="20" width="20 "></td> <td><?php echo $v["file_name"]; ?></td> <td><?php echo $v['file_time']; ?></td> <td><?php echo $v["file_path"]; ?></td> <td><?php echo $v["file_size"]; ?></td> <td><a href="?path=<?php echo $v['file_path'] ?>">打开</a></td> </tr> <?php endforeach; ?> <!-- 循环文件 --> <?php foreach ($file_list['file'] as $v): ?> <tr> <td><img src="./img/file.png" height="20" width="20 "></td> <td><?php echo $v["file_name"]; ?></td> <td><?php echo $v["file_time"]; ?></td> <td><?php echo $v["file_path"]; ?></td> <td><?php echo $v["file_size"]; ?></td> <td> <a href="?action=del&file_path=<?php echo $v['file_path'] ?>">删除</a> <a href="?action=down&file_path=<?php echo $v['file_path'] ?>">下载</a> <a href="?action=edit&file_path=<?php echo $v['file_path'] ?>">编辑</a> </td> </tr> <?php endforeach; ?> </tbody> </table> </body> </html>
|
安全问题
1、 目录遍历
如果没有设置-可访问目录-就会出现
2、 任意文件上传、删除、包含、下载
上传没有过滤:传一句话木马
还没有重命名文件:文件包含执行木马,控制网站
没有设置-可访问目录-:任意删除、下载
措施:
输入验证
限制文件类型
过滤危险字符:str_replace等。
配置open_basedir:可访问目录。
升级PHP版本
重命名上传文件:防止被读取。
设置白名单:
权限管理:最小化allow_url_include和allow_url_fopen的权限。