Day 23 PHP应用 | 后台模块 | Session | Cookie | Token | 身份验证 | 唯一性

后台模块->身份验证 Cookie Seesion Token

知识点

Cookie是身份凭证,Seesion是会话凭证;它们的作用是判定用户身份。在访问网站的时候,网站可以通过cookie确定用户身份,如果在访问的时候提供了相同的cookie,那么网站可以将两次的访问当成同一个用户访问的

在使用了cookie的站点,用户对站进行访问的时候进行以下流程

1、浏览器向服务器发起请求,然后服务器生成响应并通过set-cookie​响应头将cookie设置给浏览器

2、浏览器保存收到的cookie

3、用户访问网站的时候浏览器在请求头中附带对应的cookie发送给服务器

4、服务器读取cookie并检验cookie的有效性

5、如果cookie有效则服务器响应请求,否则要求用户重新登录

一般在涉及到网页的身份判定的话都会用到这个技术,如果获取到了Cookie那么就可以通过cookie的用户权限去访问到对应的网站

Cookie是储存到本地的浏览器的,嗯,有可能获取到浏览器的cookie?​进行恶意访问

因为cookie存储在浏览器中,只要拿到了cookie就可以仿冒身份,所以非常不安全,后面就有了session。

session控制的是一个会话,如果检测到会话超时,那么就会断开连接,就算是通过coockie拿到了sessionID,那么也无法连接到会话中,当重新对网页进行访问的时候,就会重新分配一个sessionID

session的流程

1、客户端发起http请求

2、服务器响应请求为客户端分配一个卫衣的的session ID,并储存在服务器中

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

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

5、客户端在下一次发起http请求的时候会将coockie信息附加到请求头中,以便服务器可以通过该session ID识别客户端

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

session_start 启动会话或者回复一个已经存在的会话
$_SESSION 用于储存和访问当前会话中所有变量
session_destroy 销毁当前会话中的所有数据
session_unset 释放当前会话中所有变量
session储存路径 PHP.INI中session。save_path设置路径

唯一性判断-Token使用

1、生成token并将其储存在session

2、生成token并将其绑定在cookie出发

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

4、思考token安全性

Token就是身份凭证,在用户登录之后会生成一个token,然后发送给客户端;然后客户端每次发起请求的时候都会附带这个token,服务器通过token判断用户是否有权限

token的生成与session和cookie的路程一致

teken的特点

1、无需服务器存储状态 token本身可包含身份信息,服务器可无状态处理

2、可设置过期时间 空值token的有效期,提高安全性

3、通常保存在前端 可存在local storage、session storage、cookie中

4、跨平台使用 移动端、web、API都可以使用同一个token

关于Token+Session+Cookie混合认证方式的登录流程

1、用户登录提交表单提交用户名密码给服务器

2、服务器验证通过后:

  • 生成一个随机的Token
  • 将这个Token保存在Session中
  • 通过Set-Cookie把Token写入浏览器的Cookie

3、之后客户端每次请求:

  • 浏览器会自动带上 Cookie 中的 Token(请求头里有 Cookie: token=xxxx​);
  • 服务器从 Cookie 中取出 Token;
  • 再去 Session 中找对应的 Token 进行比对,验证是否一致;
  • 一致:说明是合法用户,放行;
  • 不一致:说明 token 被篡改或非法,拒绝访问。

Token是身份标识;Session是服务器端储存当前用户状态(包含Token),可验证身份;Cookie在浏览器中储存token,自动随请求带回服务器

流程示意图

[浏览器登录]
|
└───► POST /login {username, password}
|
└──► 验证成功,生成 token
├── session['token'] = abc123
└── Set-Cookie: token=abc123

[后续请求]
|
└───► 浏览器自动附带 Cookie: token=abc123
|
└──► 服务器取出 token 与 session['token'] 比较
└── 如果一致:通过

#具体安全知识点

Cookie和Session都是在Web应用程序中跟组用户状态机制

1、存储位置不同:

Cookie是储存在客户端上的,而session是存储在服务器端的

2、安全性不同

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

3、存储容量不同

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

4、生命周期不同

Cooke可以设置过期时间,即便关闭浏览器或者重新打开电脑Cookie依然存在,知道过期或者删除,而Session一般在关闭浏览器关闭后就会过期

5、访问方式不同

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

6、使用场景不同

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

判断一个网站用的是cookie还是session可以通过登录超时时间判断,session一般超时时间比较短(半个小时左右),如果登录之后不操作等待一段时间之后登录失效了,一般用的就是session,然后还有就是session一般都会设置退出浏览器之后删除session(需要在代码中设置),如果退出之后登录失效,一般来说是session(如果代码没有设置的还还是会可以登录的)。

token的作用主要就是用来判断身份唯一性,也就是每一次登录使用的token都是唯一的,如果token不匹配那么不会进行其他操作。token是一个随机数,可以通过任意一种生成随机数的方式生成。

实验

本次实验继续使用上次的实验环境,在原项目中的admin文件夹下创建index-c.php​、admin-c.php​、logout-c.php​,其中,admin-c为登录页面,登录成功跳转index-c,点击退出之后跳转logout-c

首先在admin.php中写一个登录页面(可以使用chatgpt写)

登录代码的处理流程

1、接收到网页中用户输入的用户名和密码

2、判断账号密码是否与数据库中的数据相匹配

3.1、如果账号密码正确则生成cookie进行保存

4、账号密码验证成功跳转到对应的网页

3.3、如果账号密码错误则进行提示

<?php
//登录页面
include "../config.php";	//包含一下之前定义的config.php

//接收表单的登录数据
$user = $_POST['username'];
$pass = $_POST['password'];

$sql = "SELECT * FROM admin WHERE username = '$user' AND password = '$pass';";


if($_SERVER["REQUEST_METHOD"] == "POST") {
//判断如果表单发送了post请求才执行后面的语句(否则刷新的时候一直弹窗)
    $data = mysqli_query($con, $sql);
    if(mysqli_num_rows($data) > 0) {
	//从数据库取出数据并判断用户名
        $expire = time() +60*60*24*30;  //定义一个月过期
        setcookie('username',$user,$expire,'/');
        setcookie('password',$pass,$expire,'/');
        header('location:index_c.php');
		//通过header跳转页面
        exit();
    }else{
        echo "<script>alert('密码错误');</script>";
    }
}

这里通过超全局变量验证表单是否提交了post请求,然后执行判断用户名密码是否正确,最后如果密码正确则生成cooke值,否则弹窗提示账号或者密码错误

sql语句查询的时候,如果有匹配数据则返回数据,没有不会返回数据,然后通过mysqli_num_rows可以检查数据返回了多少行,如果返回行数大于0则表示有匹配的数据,表示账号密码正确

然后我在写这些代码的时候遇到了一些问题,就是header()函数在调用之前如果页面输出了数据则会报错,最简单的处理方法就是将php代码放在整个文件最前端,或者是使用js实现跳转

然后关于管理页面的代码,这里我自己写了一下按小迪的思路从cookie获取cookie中的username和password然后从数据去验证用户名和密码是否合法的思路,结果出现问题了,然后我突然明白为啥小瑟迪为啥要用固定的用户名密码了哈哈哈

后端管理页面比较简单,先写一个小迪课上的代码

<?php
if($_COOKIE['username'] != 'admin' and $row['password'] != 'password') {
    header('location: admin_c.php');
}
?>

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>后台首页</title>
</head>
<body>
<h1>后台首页</h1>
<P> 欢迎您 , <?php echo $_COOKIE['username'];?></P>
<p><a href = "logout_c.php">退出登录</a></p>
</body>
</html>

这里就随便写一个页面模拟一下,然后php代码就是写一个判断cookie中的username和password是否和管理员账号密码不一样,如果不一样则跳转回登录页面,如果不是不符合则正常显示网页

然后这里放一下我写的通过数据库比较用户名密码的代码

//一开始我想的是直接给一个默认值,如果有cookie则使用cookie的值
//主要是$user和$pass这里的值的处理
<?php
include "../config.php";

$user = $_COOKIE["username"] == NULL ? '1' : $_COOKIE["username"];
$pass = $_COOKIE["password"] == NULL ? '1' : $_COOKIE["password"];



$sql = "SELECT * FROM admin WHERE username = '$user' AND password = '$pass';";
$data = mysqli_query($con, $sql);

$row = mysqli_fetch_assoc($data);


if (!isset($row) || $user != $row['username'] || $pass != $row['password']) {
    header('Location: admin_c.php');
    exit();
}

代码的逻辑是通过两个变量接收cookie中的值,然后通过这两个变量拼接sql语句查询数据库,之后通过mysqli_fetch_assoc从结果中读取一行数据(默认不会发生一个用户名多次注册),然后判断数据库中的数据与cookie中的数据是否不等,如果不等则返回登录页面

当然,这里我写的比较繁琐,后面我将流程简化了一下

//首先是变量赋值那里,为了防止有人真的用了用户名:1,密码:1就炸了
$user = isset($_COOKIE['username']) ? $_COOKIE['username'] : '';
$pass = isset($_COOKIE['password']) ? $_COOKIE['password'] : '';

这里使用isset判断,如果有被操作,也就会值为真(被赋值了),则赋值为cookie中的值如果值为假则赋值为空

//然后通过查看文档发现php 7 开始支持控制合并运算符
/*
空值合并运算符(??)是PHP 7中引入的一个非常有用的运算符。它的作用是当左边的操作数为
null时,返回右边的操作数;否则返回左边的操作数。这个运算符主要用于简化对可能为null
的变量的检查和赋值
*/
$user = $_COOKIE['username'] ?? '';
$pass = $_COOKIE['password'] ?? '';

然后在if判断那里可以优化一下判断语句

if(!$row) {
    header('Location: admin_c.php');
}
//这里如果在数据库中没有查询到数据就没必要去比对了,直接可以跳回了
//没有查询到表示这个cookie是非法的

最后的代码

<?php
include "../config.php";

$user = $_COOKIE['username'] ?? '';
$pass = $_COOKIE['password'] ?? '';

if ($user == '' || $pass == '') {
    header('Location: admin_c.php');
    exit();
}

$sql = "SELECT * FROM admin WHERE username = '$user' AND password = '$pass';";

$data = mysqli_query($con, $sql);

$row = mysqli_fetch_assoc($data);


if(!$row) {
    header('Location: admin_c.php');
}


?>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>后台首页</title>
</head>
<body>
<h1>后台首页</h1>
<P> 欢迎您 , <?php echo $_COOKIE['username'];?></P>
<p><a href = "logout_c.php">退出登录</a></p>
</body>
</html>

然后在logout_c中只需要清除Cooke然后返回登录页面就可以了

<?php
setcookie('username','',time() -3600 , '/');
setcookie('password','',time() -3600 , '/');
header('location:admin_c.php');

删除cookie建议使用这种写法

session的实验,html代码依旧使用之前的代码,部分登录的php也是使用之前的代码,然后主要改变的地方是判断登录之后的代码

if($_SERVER["REQUEST_METHOD"] == "POST") {
    if(mysqli_num_rows($data) > 0) {
        session_start();
        $_SESSION['username'] = $user;
        $_SESSION['password'] = $pass;
        header('location:index_s.php');
        exit();

然后还有就是在index那边的代码略微做一下修改

<?php
include "../config.php";
session_start();		//添加
$user = $_SESSION['username'] ?? '';	//修改
$pass = $_SESSION['password'] ?? '';	//修改

if ($user == '' || $pass == '') {
    header('Location: admin_s.php');	//修改文件名
    exit();
}

$sql = "SELECT * FROM admin WHERE username = '$user' AND password = '$pass';";

$data = mysqli_query($con, $sql);

$row = mysqli_fetch_assoc($data);


if(!$row) {
    header('Location: admin_s.php');	//修改文件名
}


?>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>后台首页</title>
</head>
<body>
<h1>后台首页</h1>
<P> 欢迎您 , <?php echo $_SESSION['username'];?></P>
<p><a href = "logout_s.php">退出登录</a></p>		//修改文件名
</body>
</html>

logout

<?php
session_start();		//启动session
session_destroy();		//清除session会话数据
session_unset();		//释放session变量
header("location: admin_s.php");

token实验

token储存在session中,所以这里代码基本还是使用session的代码,基本代码可以继续copy之前的代码

<?php
session_start();
$token = bin2hex(random_bytes(16));
//在本地生成token
$_SESSION['token'] = $token;
//将token保存到session中
setcookie('token', $_SESSION['token'], time() + 3600,'/');
//token绑定到cookie中
?>

生成token并保存token然后绑定到cookie中

//这里主要是修改部分的代码
    <form class="login-form" id="loginForm" method="post" action="token_check.php">
	<!--       这里需要将表单提交到token_check.php文件中判断       -->
        <input type="hidden" name = 'token' value="<?php echo $_SESSION['token']; ?>">
        <!--       通过一个表单提交token,设置kidden标识这个表单是隐藏的       -->
<?php
session_start();
$token = $_COOKIE['token']; //这里是用户通过cookie发送的token

if ($token !== $_SESSION['token']) {    //这里是通过服务器中获取的token     //如果服务端和客户端之间的token不匹配则返回403有页面
    header('HTTP/1.1 403 Forbidden');
    $_SESSION['token'] = bin2hex(random_bytes(16));
    echo 'Access denied';
    exit;
}else{  //如果匹配则生成新的token并开始验证用户名和密码
    $_SESSION['token'] = bin2hex(random_bytes(16));
    if($_POST['username'] =='admin' && $_POST['password'] =='admin'){
        echo '登录成功';
        echo '你是管理员可以访问文件管理页面!';
    }else{
        echo '登录失败';
    }
}

通过抓包验证token的唯一性

//如果出现了和我一样设置了代理但是无法抓到包可以直接用内置浏览器访问网页

开始登录然后将报文发送到sequencer,然后去重放器(如果是英文版本的就是requencer),然后点击发送(send)然后可以看到登录成功了

然后再点击发送可以看到这时候因为token和服务器中的token不一样了所以返回了403(access denied)

每次服务器收到登录请求之后(按照我们写的代码的环境中)无论登录成功或者失败服务器中的token都会重新生成,然后因为客户端的token没有修改,所以判断结果为true,所以返回了403状态码

然后尝试使用爆破

首先点击清除,然后选择密码,之后点击添加

然后在这里添加密码,其中有一个是正确密码,然后点击开始攻击

然后可以看到结果,这里虽然最后的密码是正确的,但是因为token没有发生改变,所以还是返回了403

在浏览器中,每次登录的时候都会自动刷新token值,所以账号密码正确的话就可以直接登录上,但是在Burp中token值是一直不会发生变化的,所以账号密码都正确的时候,也无法登录成功

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值