原生开发

课堂完结后欲复习巩固也方便后续-重游-故写此篇
从实现功能过渡到涉及的相关知识点

留言板功能

简单流程:浏览器访问 -> 后端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";
// 会返回mysql
$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>";

//sql语句
$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语句(拼接)
$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;

没有@结果是

没有@的运行结果
有@:
有@的运行结果


也没特别的流程,直接上代码:
首先是登录页面的代码,一个简单的表单提交给 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 #ccc;
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服务!!!cookie里面有 PHPSESSID 所以
@session_start();
//开始判断
if($_POST) {
echo "1".$_SESSION['token']."<br>";
echo "2".$_POST['privatetoken']."<br>";
if ($_POST['privatetoken'] == $_SESSION['token']) {
//删除 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的文件操作无非文件读取、接收、修改、保存一类。跟着课堂写了一个浏览器显示操作的文件编辑器。能够下载、上传、修改、删除文件。

系统运行图片

知识点

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!=".."){
//文件类型(dir,file)和路径
$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
);


}
}


/**
* ======
* ======路径的过滤操作
* ======1、php自带的ini文件里设置最大路径
* ======2、php的可访问目录函数把范围框在这个文件夹
* ======ini_set('open_basedir',_DIR_)
*/

ini_set('open_basedir','.');

if(is_dir($dir)){
show_file($dir);
}else{
dir('路径请求错误!不是文件夹路径!');
}



/**
* ======
* ======文件的编辑,删除,下载函数
* ======
* ======
*/

//删除函数
function delete($file_path){
//使用php自带函数删除,避免使用system()系统
// system("del $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的权限。