web安全代码基础-PHP(身份验证技术)

身份验证技术基本概念

1、Cookie:

  • 存储位置:客户端浏览器(文本文件)
  • 特点:
    1. 数据保存在用户本地,每次请求自动携带发给服务器
    2. 大小限制约 4KB,不安全,用户可篡改、删除
    3. 无过期设置则浏览器关闭自动销毁;可设置有效期持久保存
    4. 适合存储非敏感数据:记住用户名、浏览记录

2、Session:

  • 存储位置:服务器端(文件 / Redis / 数据库)
  • 特点:
    1. 仅在客户端存一个唯一 PHPSESSID Cookie,真正数据在服务端
    2. 安全,用户无法直接修改会话数据
    3. 浏览器关闭,默认 Session 失效;可手动销毁
    4. 适合敏感数据:登录状态、用户 ID、权限
两者的原理过程

1、生成cookie的原理过程:

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

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

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

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

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

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

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

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

2、生成session的原理过程:
1、客户端向服务器发送HTTP请求。

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

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

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

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

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

两者的代码示例

1、PHP Cookie 完整示例:

php 复制代码
<?php
// 1. 设置Cookie:setcookie() 必须在任何输出前执行(不能有echo、html空格)
// 参数:名称, 值, 过期时间, 路径, 域名, https, httponly

// 示例1:关闭浏览器就失效的cookie
setcookie("username", "zhangsan");

// 示例2:有效期1小时(当前时间+3600秒)
$expire = time() + 3600;
setcookie("user_token", "abc123xyz", $expire);

// 示例3:全站生效、仅https、禁止js读取(防XSS)
setcookie("safe_cookie", "data123", $expire, "/", "", true, true);

// 2. 读取Cookie:超全局 $_COOKIE
echo "读取Cookie:<br>";
if(isset($_COOKIE['username'])){
    echo "用户名:" . $_COOKIE['username'] . "<br>";
}
if(isset($_COOKIE['user_token'])){
    echo "令牌:" . $_COOKIE['user_token'] . "<br>";
}

// 3. 删除Cookie:设置过期时间为过去时间
setcookie("username", "", time() - 3600);
echo "已删除username Cookie";
?>

2、PHP Session 完整示例:

php 复制代码
<?php
// 第一步:开启会话,必须放在代码最顶部,无任何输出
session_start();

// 1. 写入Session(服务端存储)
$_SESSION['user_id'] = 1001;
$_SESSION['nickname'] = "小王";
$_SESSION['is_login'] = true;

// 2. 读取Session
echo "=== Session数据 ===<br>";
if(isset($_SESSION['is_login']) && $_SESSION['is_login']){
    echo "用户ID:" . $_SESSION['user_id'] . "<br>";
    echo "昵称:" . $_SESSION['nickname'] . "<br>";
    echo "登录状态:已登录<br>";
}

// 查看浏览器自动生成的Session Cookie(PHPSESSID)
echo "<br>当前会话ID:" . session_id();

// 3. 删除单个Session变量
unset($_SESSION['nickname']);

// 4. 清空全部Session数据
session_unset();

// 5. 彻底销毁会话(清除服务端文件 + 客户端PHPSESSID cookie)
session_destroy();

// 清除本地保存的session id cookie
setcookie(session_name(), "", time()-3600, "/");
?>

3、结合登录实战案例:

php 复制代码
//login.php
<?php
session_start();

// 模拟账号密码
$user = [
    "name" => "admin",
    "pwd" => "123456"
];

if($_POST){
    $name = $_POST['name'];
    $pwd = $_POST['pwd'];
    if($name == $user['name'] && $pwd == $user['pwd']){
        // 登录成功:Session存登录状态(服务端安全)
        $_SESSION['login_user'] = $name;
        $_SESSION['login_time'] = time();

        // Cookie记住用户名(前端持久,下次自动填充)
        setcookie("remember_name", $name, time()+86400*7); // 7天

        header("Location: index.php");
        exit;
    }else{
        $msg = "账号密码错误";
    }
}
?>

<!DOCTYPE html>
<form method="post">
    用户名:<input name="name" value="<?php echo $_COOKIE['remember_name']??'' ?>"><br>
    密码:<input type="password" name="pwd"><br>
    <button type="submit">登录</button>
    <?php echo $msg??'' ?>
</form>
php 复制代码
//index.php(验证登录)
<?php
session_start();
// 判断Session是否存在登录标识
if(!isset($_SESSION['login_user'])){
    header("Location: login.php");
    exit;
}
$user = $_SESSION['login_user'];
echo "欢迎你,{$user}!已登录";
echo "<br><a href='logout.php'>退出登录</a>";
?>
php 复制代码
//lodinout.php(退出销毁会话)
<?php
session_start();
// 清空session
session_unset();
session_destroy();
// 删除记住用户名的cookie
setcookie("remember_name", "", time()-1, "/");
header("Location: login.php");
exit;
?>
两者存在的安全问题

1、Cookie脆弱问题

Cookie 存在两大安全短板:未设置 HttpOnly,JS 可直接读取 Cookie(XSS 攻击偷凭证);Cookie 存储在客户端,用户可手动修改内容伪造身份

演示:XSS 窃取 Cookie(脆弱点 1)

php 复制代码
//后端 setcookie 未加 HttpOnly(危险写法)
<?php
// 危险:没有第七个参数httponly=true,JS能读取cookie
setcookie("user_token", "admin_token_123", time()+3600, "/");
?>

<!-- 存在XSS漏洞页面 -->
<div>
    用户留言:<?php echo $_GET['msg']; ?>
</div>

<!-- 攻击者传入URL:?msg=<script>alert(document.cookie)</script> -->
<script>
    // 直接读出 Cookie,拿到登录凭证
    console.log(document.cookie);
</script>

修复方案(开启 HttpOnly,JS 无法读取):

php 复制代码
// 第七个参数 true = HttpOnly,阻断JS读取Cookie
setcookie("user_token", "admin_token_123", time()+3600, "/", "", false, true);

演示:客户端篡改 Cookie(脆弱点 2)

php 复制代码
//服务端错误逻辑:直接信任 Cookie 中的身份,不做服务端校验
<?php
// 危险逻辑:以Cookie里的role判断管理员
setcookie("role", "user", time()+3600, "/");

// 业务校验
if ($_COOKIE['role'] === 'admin') {
    echo "欢迎超级管理员,可以删除所有数据";
} else {
    echo "普通用户,无删除权限";
}
?>
//攻击过程:用户打开浏览器开发者工具 → 应用 / Cookie,手动把 role 值从 user 改成 admin,刷新页面直接获得管理员权限。

核心根源(修复方案):根源:Cookie 在客户端可自由修改,不能存可信权限、身份数据

2、Session固定问题

Session 固定:攻击者提前给用户植入一个已知的 PHPSESSID,用户登录后服务器仍沿用这个会话 ID,攻击者使用同一个 SESSIONID 即可劫持用户登录会话。

漏洞成因:登录前后不重新生成 session_id

演示:Session 固定攻击

php 复制代码
//login.php(危险代码,未 regenerate_id)
<?php
session_start();
// 攻击者提前通过链接植入会话ID:xxx.com/login.php?PHPSESSID=hack123456

$name = $_POST['name'] ?? '';
$pwd = $_POST['pwd'] ?? '';

// 账号密码正确,直接写入session,不更换会话ID
if ($name === "admin" && $pwd === "123456") {
    $_SESSION['is_login'] = true;
    $_SESSION['username'] = "admin";
    header("Location: index.php");
    exit;
}
?>
<form method="post">
用户名:<input name="name">
密码:<input type="password" name="pwd">
<button>登录</button>
</form>
php 复制代码
//index.php
<?php
session_start();
if (!$_SESSION['is_login']) {
    exit("未登录");
}
echo "管理员后台,可操作敏感数据";

攻击流程:

  • 攻击者构造链接:http://xxx.com/login.php?PHPSESSID=hack123456 发给受害者
  • 受害者点开链接,浏览器 Cookie 被写入 PHPSESSID=hack123456(此时有两者情况:1、未登录时直接就行下一步;2、已经登录时,URL 传的 PHPSESSID 优先级 > Cookie 里的 PHPSESSID,但是此时需要受害者重新登录,才能绑定危险Session)
  • 受害者输入账号密码登录,服务器没有换新 session_id ,继续使用 hack123456
  • 攻击者在自己浏览器设置相同 PHPSESSID=hack123456,访问 index.php,直接窃取登录会话

修复方案:登录成功后强制重新生成会话 ID

php 复制代码
//修改 login.php 登录成功分支,添加 session_regenerate_id(true)
<?php
session_start();

$name = $_POST['name'] ?? '';
$pwd = $_POST['pwd'] ?? '';

if ($name === "admin" && $pwd === "123456") {
    // 关键防御:生成全新session_id,删除旧会话文件,抵御Session固定
    session_regenerate_id(true);
    
    $_SESSION['is_login'] = true;
    $_SESSION['username'] = "admin";
    header("Location: index.php");
    exit;
}
?>

session_regenerate_id(true) 作用:创建全新随机会话 ID;true 参数:同步删除服务器上旧的 Session 文件 攻击者提前植入的旧 SESSIONID 彻底失效,无法劫持会话。

参考链接

Cookie脆弱问题:【代码审计】xhcms-脆弱鉴权逻辑-CSDN博客

Session固定问题:【代码审计】yxcms从伪XSS到Getshell-先知社区

补充(Token)

Token:Token 翻译:令牌、凭证 是服务端发给客户端的一串加密 / 唯一字符串 ,用来证明身份,替代传统 Cookie+Session 做登录鉴权。最主要的一点。Token适用于前后端分离状态下的鉴权,如APP、小程序等

工作流程:

  • 用户提交账号密码到服务端;
  • 服务端校验账号正确后,把 用户ID、用户名、过期时间 等信息加密生成 Token;
  • 返回 Token 给前端(浏览器 / APP / 小程序);
  • 前端把 Token 存在 localStorage / Cookie;
  • 之后每次接口请求,请求头带上 Token(Authorization: Bearer xxx);
  • 服务器接收请求,解密 / 校验 Token:
    • 合法:放行接口,识别当前登录用户
    • 过期 / 篡改:返回 401 未授权,跳登录页

Token和Session的核心区别:和Cookie区别比较明显,就是Cookie是"容器"。主要是和Session的区别,

  • Session:客户端传 ID,服务端查表拿用户数据;
  • Token:客户端传打包好、带防伪签名的完整身份信息,服务端验签即用,不用查表。(服务器压力变小)

Cookie就是存Session的ID,和Token的完整身份信息的"容器"。