0%

第28天-PHP应用&Cookie脆弱&Session固定&Token唯一&身份验证&数据库通讯

演示案例:

➢ WEB开发-原生PHP-通讯数据库

➢ WEB开发-Cookie&Session&Token

➢ WEB开发-身份验证-代码审计案例

一、数据库操作 验证用户

login.php->index.php

1、login.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
<?php
/*
1、连接数据库
2、选择数据库中的表
3、接收提交的用户和密码
4、执行sql语句判断用户密码是否正确
*/

include './config.php';
$user=@$_POST['username'];
$pass=@$_POST['password'];
$con = mysqli_connect(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$sql="select * from admin where username='$user' and password='$pass'";

if($_SERVER["REQUEST_METHOD"] == "POST"){
$data=mysqli_query($con,$sql);//第一个参数是mysqli_connect()函数返回的句柄。第二个参数是sql语句
if(mysqli_num_rows($data) > 0){
#mysqli_num_rows()查询结果集有多少条行数
header('Location: index.php');
exit();
}else{
echo "<script>alert('登录失败')</script>";
}

}
重点理解标色的四行即可,三个函数,mysqli_connect()数据库连接函数,一共六个参数常用的就这四个,数
据库地址,账户名,密码,数据库,第五个端口默认不用如果mysql换了端口就要写了,第六个unix套接字文
件路径。mysqli_query()是向数据库发送一条sql语句拿到执行结果,两个参数数据库连接句柄和sql语句,
mysqli_num_rows()查询结果集有多少条行数。一个逻辑,if($_SERVER["REQUEST_METHOD"] == "POST")
发送post包后才进入if内部防止一访问这个页面就谈报错

2、index.php:

就是模拟一个后台的一个功能点,这里是后台页面,目前这里没写代码。所以现在这里没有鉴权操作有未授权漏洞,下面会完善

3、config.php:

这里写了一些数据库的固定数据,单开一个文件就是方便其他文件调用

1
2
3
4
5
6
7
8
<?php
define('DB_HOST','localhost');
define('DB_USER','root');
define('DB_PASS','123456');
define('DB_NAME','test28');

//这里定义了四个常量都是mysqli_connet()函数的参数
?>

二、Cookie验证 后台登录

cookie是本地存储,已被窃取

loginc.php->indexc.php->loginc_out.php

1、loginc.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
<?php
/*
1、连接数据库
2、选择数据库中的表
3、接收提交的用户和密码
4、执行sql语句判断用户密码是否正确
*/

include './config.php';
$user=@$_POST['username'];
$pass=@$_POST['password'];
$con = mysqli_connect(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$sql="select * from admin where username='$user' and password='$pass'";

if($_SERVER["REQUEST_METHOD"] == "POST"){
$data=mysqli_query($con,$sql);//第一个参数是mysqli_connect()函数返回的句柄。第二个参数是sql语句
if(mysqli_num_rows($data) > 0){
#mysqli_num_rows()查询结果集有多少条行数
$expier = time()+60*60*24; //一天过期
setcookie('username',$user,$expire,'/');
setcookie('password',$pass,$expire,'/');
header('Location: indexc.php');
exit();
}else{
echo "<script>alert('登录失败')</script>";
}

}
其他位置都和上面的login.php一样,只有标色部分添加了内容,$expier是过期时间,time()是当前时间,后
面加的是秒,所以整个一句就是设置过期时间为一天,二三行都是设置cookie,setcookie(键,值,过期时间
,生效目录)。下一句就是跳转到indexc.php

2、indexc.php

1
2
3
4
5
6
7
8
9
10
11
<?php

if(@$_COOKIE['username']=='xiaodi' and @$_COOKIE['password']=='xiaodisec'){
echo '恭喜进入管理员后台!';
echo '<a href="loginc_out.php">退出</a>';
}else{
echo '请登录后尝试!';
}
这里就和之前index.php不一样了,这里加了鉴权操作,就是判断cookie是否为loginc.php,当然这里是写死
的正常业务逻辑这里是变量,echo '<a href="loginc_out.php">退出</a>';这个是一个超链接,点一下相当
于跳到loginc_out.php了,loginc_out.php是一个删除cookie的操作就是模拟退出

3、loginc.out.php

1
2
3
4
5
<?php
setcookie('username',$user,time()-1,'/');
setcookie('password',$pass,time()-1,'/');
header('Location: loginc.php');
过期时间改成time()-任意数,这个就是销毁cookie,值置不置空都行,然后重定向到登录页面

三、Session验证 后台登录

session存储在服务端,也能被窃取,但是cookie能被篡改,session不行,一定程度上防范了越权登录其他用户

logins.php->indexs.php->logins_out.php

1、logins.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
<?php
/*
1、连接数据库
2、选择数据库中的表
3、接收提交的用户和密码
4、执行sql语句判断用户密码是否正确
*/

include './config.php';
$user=@$_POST['username'];
$pass=@$_POST['password'];
$con = mysqli_connect(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$sql="select * from admin where username='$user' and password='$pass'";

if($_SERVER["REQUEST_METHOD"] == "POST"){
$data=mysqli_query($con,$sql);//第一个参数是mysqli_connect()函数返回的句柄。第二个参数是sql语句
if(mysqli_num_rows($data) > 0){
#mysqli_num_rows()查询结果集有多少条行数
session_start();
$_SESSION['username']=$user;
$_SESSION['password']=$pass;
header('Location: indexs.php');
exit();
}else{
echo "<script>alert('登录失败')</script>";
}

}
session_start()开启会话,剩下两行就是设置session数据

2、indexs.php

1
2
3
4
5
6
7
8
9
<?php
session_start();
if(@$_SESSION['username']=='xiaodi' and @$_SESSION['password']=='xiaodisec'){
echo '恭喜进入管理员后台!';
echo '<a href="logins_out.php">退出</a>';
}else{
echo '请登录后尝试!';
}
这个和indexc.php不一样的点就是把COOKIE换成SESSION

3、logins_out.php

1
2
3
4
5
6
<?php
session_start();//消除session必须先销毁session
session_unset();//删除所有session数据,还可以unset($_SESSION[名]),删除单个session数据
session_destroy();//删除session文件
header('Location: logins.php');

四、Session+Token 防爆破登录

loginst.php->logincheck.php->indexst.php->loginst_out.php

1、loginst.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
<?php
// 生成Token并将其存储在Session中
include 'configs.php';


//1.因为是用的session维持会话,token已经绑定到下面的表单了
//2.token,生成之后直接存到session里,主要是方便重置token,
//每次token随表单提交后都需要重置以保持token的唯一性。

$_SESSION['token'] = bin2hex(random_bytes(32));


?>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>后台登录</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="login-container">
<div class="login-box">
<h2>后台登录</h2>
<form action="logincheck.php" method="POST">
<input type="hidden" name="token" value="<?php echo $_SESSION['token'] ; ?>">
<div class="input-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username" placeholder="请输入用户名" required>
</div>
<div class="input-group">
<label for="password">密码</label>
<input type="password" id="password" name="password" placeholder="请输入密码" required>
</div>
<button type="submit" class="btn">登录</button>
<div class="footer">
<p>忘记密码?</p>
<p>还没有账号? <a href="regedit.php">注册</a></p>
</div>
</form>
</div>
</div>
</body>
</html>
这个页面的作用是生成一个64位的十六进制字符串,下面表单中让这个十六进制字符串也就是token嵌入,这
里的token指的是csrf token,用于身份验证的是jwt,csrf tokn是用来防止爆破的。

2、configs.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
session_start();

// 生成 CSRF Token(如果没有)
if (empty($_SESSION['token'])) {
$_SESSION['token'] = bin2hex(random_bytes(32)); // 生成一个 32 字节的 token
}

// 数据库配置信息
define('DB_HOST', 'localhost'); // 数据库主机
define('DB_USER', 'root'); // 数据库用户名
define('DB_PASS', '123456'); // 数据库密码
define('DB_NAME', 'phpstudy'); // 数据库名称
这里也是生成token,然后定义数据库连接的数据

3、logincheck.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
<?php
include 'configs.php';
$conn=mysqli_connect(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$user=@$_POST['username'];
$pass=@$_POST['password'];
$token = @$_POST['token'];

$sql="select * from admin where username='$user' and password='$pass';";
$data=mysqli_query($conn,$sql);

if($_SERVER["REQUEST_METHOD"] == "POST"){
if (empty($token) || $token!== $_SESSION['token']) {
echo '<script>alert("Token 无效!")</script>';
exit();
}
//判断用户登录成功
if(mysqli_num_rows($data) > 0){
session_start();
$_SESSION['username']=$user;
$_SESSION['password']=$pass;
$_SESSION['token'] = bin2hex(random_bytes(32));
header('Location: indexst.php');
exit();
}else{
//判断用户登录失败
echo '<script>alert("登录失败!")</script>';
}
}


?>
首先是连接数据库去查用户名和密码,这里有一个逻辑是判断token是否为空或者token的值对不对的上,每
次刷新页面csrf token都会刷新,所以爆破行为得到抑制。

4、indexst.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php


session_start();

if(@$_SESSION['username']=='xiaodi' && @$_SESSION['password']=='xiaodisec'){
echo "恭喜进入后台管理页面!";
echo '<a href="loginst_out.php">退出</a>';
}else{
echo '<script>alert("请登录!")</script>';
//header('Location: loginc.php');
}


?>
这个和之前的没什么区别,就是退出登录的超链接地址不一样

5、loginst_out.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

// 开始会话
session_start();

// 清除 SESSION 变量,并销毁会话
session_unset();
session_destroy();

// 重定向到登录页面
header('Location: loginst.php');
exit;

这一步和logins_out.php一样就是销毁SESSION,因为这里是SESSION来维持会话。

五、两例代码审计-Cookie&Session

1、XHCMS Cookie脆弱

admin目录索引文件(默认首页)

1
2
3
4
5
6
7
8
9
10
<?php
//单一入口模式
error_reporting(0); //关闭错误显示
$file=addslashes($_GET['r']); //接收文件名
$action=$file==''?'index':$file; //判断为空或者等于index
include('files/'.$action.'.php'); //载入相应文件
?>

//这里第四行是三元运算符,条件?条件成立的值:不成立的值。r的值为空的话就是index,写值的话就是那个值。
然后包含files/r的值.php

我们直接访问xhcms.com/admin就相当于访问了xhcms.com/admin/

index.php。因为没带r所以就访问了admin/files/index.php这也就是上面代码的逻辑。

admin/files/index.php

1
2
3
4
5
6
<?php
require '../inc/checklogin.php';
require '../inc/conn.php';
$indexopen='class="open"';
?>
后面有一个登录表单这里不显示了

观察这个代码一个是数据库连接文件还有一个就是检测登录状态的文件,肯定是这个checklogin.php重要。

admin/inc/checklogin.php

1
2
3
4
5
6
7
8
9
<?php
$user=$_COOKIE['user'];
if ($user==""){
header("Location: ?r=login");
exit;
}
?>
这个就是检查是否携带cookie,如果没有就重定向到login。漏洞点就在这里,index.php引入了这个文件,只
要cookie不为空就能进入

我随便设置了user=1就登进来了,正常你不登录的话是访问不了这个页面的

2、YXCMS Session固定

https://xz.aliyun.com/t/2025这个就看文章吧偷个懒

后面都是些知识点:

身份验证-Cookie使用

生成cookie的原理图过程:

1、客户端向服务器发送HTTP请求。

2、服务器检查请求头中是否包含cookie信息。

3、如果请求头中包含cookie信息,则服务器使用该cookie来识别客户端,否则服务器将生成一个新的cookie。

4、服务器在响应头中设置cookie信息并将其发送回客户端。

5、客户端接收响应并将cookie保存在本地。

6、当客户端发送下一次HTTP请求时,它会将cookie信息附加到请求头中。

7、服务器收到请求并检查cookie的有效性。

8、如果cookie有效,则服务器响应请求。否则,服务器可能会要求客户端重新登录。

$_COOKIE:是一个关联数组,包含通过cookie传递给当前脚本的内容。

setcookie(): 设置一个cookie并发送到客户端浏览器。

unset(): 用于删除指定的cookie。

身份验证-Session使用

1、客户端向服务器发送HTTP请求。

2、服务器为客户端生成一个唯一的session ID,并将其存储在服务器端的存储器中(如文件、数据库等)。

3、服务器将生成的session ID作为一个cookie发送给客户端。

4、客户端将session ID保存为一个cookie,通常是在本地浏览器中存储。

5、当客户端在发送下一次HTTP请求时,它会将该cookie信息附加到请求头中,以便服务器可以通过该session ID来识别客户端。

6、服务器使用session ID来检索存储在服务器端存储器中的与该客户端相关的session数据,从而在客户端和服务器之间共享数据。

session_start(): 启动会话,用于开始或恢复一个已经存在的会话。

$_SESSION:是一个关联数组,包含当前脚本中的所有session内容。

session_destroy(): 销毁当前会话中的所有数据。

session_unset(): 释放当前会话中的所有变量。

Session存储路径:PHP.INI中session.save_path设置路径

唯一性判断-Token使用

1、生成Token并将其存储在Session

2、生成Token并将其绑定在Cookie触发

3、尝试登录表单中带入Token验证逻辑

4、思考Token安全特性

具体安全知识点:

-Cookie和Session都是用来在Web应用程序中跟踪用户状态的机制

1、存储位置不同:

Cookie是存储在客户端(浏览器)上的,而Session是存储在服务器端的。

2、安全性不同:

Cookie存储在客户端上,可能会被黑客利用窃取信息,而Session存储在服务器上,更加安全。

3、存储容量不同:

Cookie的存储容量有限,一般为4KB,而Session的存储容量理论上没有限制,取决于服务器的硬件和配置。

4、生命周期不同:

Cookie可以设置过期时间,即便关闭浏览器或者重新打开电脑,Cookie仍然存在,直到过期或者被删除。而Session一般默认在浏览器关闭后就会过期。

5、访问方式不同:

Cookie可以通过JavaScript访问,而Session只能在服务器端进行访问。

6、使用场景不同:

Cookie一般用于存储小型的数据,如用户的用户名和密码等信息。而Session一般用于存储大型的数据,如购物车、登录状态等信息。

总之,Cookie和Session都有各自的优缺点,选择使用哪一种方式,取决于具体的应用场景和需求。一般来说,如果需要存储敏感信息或者数据较大,建议使用Session;如果只需要存储少量的数据,并且需要在客户端进行访问,可以选择使用Cookie。

-在Web应用程序中,使用token和不使用的主要差异在于身份验证和安全性。

1.身份验证:采用token机制的Web应用程序,用户在登录成功后会收到一个token,这个token可以在每次请求时发送给服务器进行身份验证。而不采用token机制的Web应用程序,一般会使用session机制来保存用户登录状态,服务器会在用户登录成功后创建一个session,之后的每个请求都需要在HTTP头中附带这个session ID,以便服务器能够验证用户身份。

2、安全性:采用token机制的Web应用程序,在服务器上不会存储用户的登录状态,只需要存储token即可。因此,即使token被盗取,黑客也无法获得用户的密码或者其他敏感信息。而不采用token机制的Web应用程序,一般会在服务器上存储用户的登录状态,因此如果服务器被黑客攻击,黑客可能会获得用户的敏感信息。

3、跨域访问:采用token机制的Web应用程序,在跨域访问时,可以使用HTTP头中的Authorization字段来传递token信息,方便实现跨域访问。而不采用token机制的Web应用程序,在跨域访问时,需使用cookie或session来传递用户身份信息,比较麻烦。

总之,采用token机制可以提高Web应用程序的安全性,并且方便实现跨域访问。不过,使用token机制也需要开发者自己来实现身份验证和token的生成和验证,相对来说比较复杂。而不采用token机制,使用session机制则相对简单,但是安全性相对较低。因此,具体采用哪种机制,需要根据实际情况进行权衡和选择。