网络空间安全深度实战:CSRF 漏洞原理剖析与基于 Token 的纵深防御体系构建(全栈实验报告)

全栈实验报告​

**适用场景:**​ 信息安全专业毕业设计 / 顶级高校实验报告 / 企业级安全开发规范

**核心技术:**​ PHP / Session / CSRF Token / Burp Suite / OWASP / DVWA 源码审计

摘要

随着互联网业务的爆炸式增长,Web 安全已从单一的代码漏洞防护演变为体系化的对抗工程。在 OWASP Top 10 的历史长河中,CSRF(Cross-Site Request Forgery,跨站请求伪造)作为一种利用"浏览器隐式身份验证机制"的攻击手段,长期占据重要地位。

本报告摒弃传统的"脚本小子"式复现,转而从协议层的信任机制缺陷 入手,通过构建一个最小化但功能完备的 PHP 靶场,完整还原了 CSRF 漏洞的产生、利用及防御全过程。实验重点实现了基于 Session 绑定的 Synchronizer Token Pattern(同步令牌模式),并结合 Burp Suite 进行严格的攻击复现与防御验证。

报告深入对比了 DVWA(Damn Vulnerable Web Application)中 Low、Medium、High、Impossible 四个等级的源码逻辑,剖析了 Referer 校验的绕过原理与 Token 的一次性生命周期管理。最终,本文提出了一套适用于现代微服务架构下的 CSRF 纵深防御体系,为企业级安全开发提供理论依据与实践指南。

**关键词:**​ Web 安全;CSRF;Token 防御;Session 固化;OWASP;源码审计;Burp Suite

第一章 绪论

1.1 研究背景与意义

Web 应用程序的安全性一直是网络安全领域的核心议题。根据 Akamai 2024 年的互联网安全报告显示,API 和 Web 攻击同比增长了 49%,其中利用业务逻辑漏洞的攻击占比显著提升。

CSRF 攻击之所以危险,是因为它不需要窃取用户的凭据(Cookie/Session) ,而是欺骗用户的浏览器,利用其已登录的信任状态,在受害者毫不知情的情况下,以受害者的身份执行非预期的操作。例如:

  1. **金融领域:**​ 伪造银行转账请求。

  2. **社交工程:**​ 篡改用户绑定的邮箱或手机号。

  3. **基础设施:**​ 在路由器或 IoT 设备管理界面中修改 DNS 设置。

因此,深入研究 CSRF 的防御机制,不仅是修复一个漏洞,更是对 Web 身份鉴别与授权体系的加固。

1.2 国内外研究现状

早期防御手段多依赖于 Referer 头部校验 ​ 或 Custom Header (如 X-Requested-With)。然而,随着攻击技术的演进,Referer 可被伪造或被浏览器策略省略(如 no-referrer)。现代安全社区普遍推崇 Synchronizer Token Pattern。OWASP 明确指出,任何改变服务器状态的请求(State-Changing Requests)都必须实施 Anti-CSRF Token 机制。

1.3 实验目标

本实验旨在达成以下目标:

  1. **原理复现:**​ 搭建一个存在 CSRF 风险的 Web 环境。

  2. **机制构建:**​ 手动实现基于 PHP Session 的 Token 生成、分发、校验与销毁全流程。

  3. **攻击验证:**​ 使用 Burp Suite 模拟重放攻击,验证无防御状态下的脆弱性。

  4. **防御评估:**​ 验证 Token 的一次性有效性,分析其抗绕过能力。

  5. **源码对标:**​ 对比 DVWA 靶场源码,提升代码审计能力。

第二章 Web 身份认证与 CSRF 理论基础

2.1 HTTP 协议的无状态性与会话管理

HTTP 协议是无状态的(Stateless)。这意味着服务器默认无法区分两个连续的请求是否来自同一个用户。为了解决这个问题,Web 开发引入了 Session-Cookie 机制

  1. **握手:**​ 用户登录,服务器验证凭据。

  2. 创建 Session: ​ 服务端创建一个 Session 对象,存储在内存或数据库中,并生成一个唯一的 Session ID

  3. 下发 Cookie: ​ 服务器通过 Set-Cookie响应头,将 Session ID发送给浏览器。

  4. **自动携带:**​ 此后,浏览器对该域的所有请求都会自动携带该 Cookie。

这正是 CSRF 的温床。 ​ 因为浏览器自动 携带 Cookie,服务器无法区分这个请求是用户主动点击 发出的,还是恶意脚本诱导发出的。

2.2 CSRF 攻击模型

CSRF 攻击成立需要三个必要条件:

条件 描述 本实验对应点
用户登录 用户处于登录状态,Session 有效。 login1.php写入 $_SESSION["admin"]=1
参数可预测 请求参数简单,攻击者能构造出合法的请求包。 add-pass.php?password_new=xxx
浏览器自动认证 浏览器会自动发送 Cookie。 Firefox 自动携带 PHPSESSID。

攻击时序图(Sequence Diagram):

复制代码
攻击者站点                          受害者浏览器                          目标服务器
    |                                   |                                     |
    |-------- 1. 诱导访问恶意页面 ------->|                                     |
    |                                   |----- 2. 发起伪造请求 (带Cookie) ----->|
    |                                   |                                     |---- 3. 执行操作(改密码) ----|
    |                                   |<---- 4. 返回结果 ---------------------|

2.3 同源策略(SOP)的误区

很多初学者会问:"不是有同源策略吗?为什么还能跨站请求?"

澄清: ​ 同源策略限制的是 JavaScript 的读写权限 (如AJAX 请求返回的数据),而不限制 HTML 标签发送请求<img>, <script>, <form>)。CSRF 恰恰是利用 <form><img>发送请求,服务器执行了操作,只是攻击者拿不到返回结果而已(盲打)。

第三章 实验环境与靶场搭建

3.1 软硬件环境配置

为了保证实验的可复现性,以下是标准化的实验环境配置:

组件 规格/版本 备注
操作系统 Windows 10 / 11 靶机环境
Web 服务 PHPStudy (小皮面板) 集成 Apache + PHP 7.3+
数据库 无 (使用文件存储) 简化环境,聚焦逻辑
浏览器 Firefox ESR 配置代理方便
渗透工具 Burp Suite Professional/Community 用于抓包与重放
代码编辑器 VS Code / Notepad++ UTF-8 编码

3.2 目录结构与代码实现

所有代码存放于 C:/phpstudy/phptutorial/WWW/csrf/目录下。

3.2.1 登录前端:login.html

这是攻击的入口,模拟管理员登录界面。

复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Admin Login - CSRF Lab</title>
    <style>
        body { font-family: Arial, sans-serif; background: #f4f4f4; }
        .login-box { width: 300px; margin: 100px auto; padding: 20px; background: #fff; border-radius: 5px; box-shadow: 0 0 10px rgba(0,0,0,.1); }
        input { width: 100%; padding: 8px; margin: 5px 0 10px; box-sizing: border-box; }
        input[type=submit] { background: #007bff; color: white; border: none; cursor: pointer; }
    </style>
</head>
<body>
    <div class="login-box">
        <h2>Administrator Login</h2>
        <!-- 表单提交至 login1.php -->
        <form action="login1.php" method="POST">
            <label>username:</label>
            <input type="text" name="username" placeholder="Enter admin">
            
            <label>password:</label>
            <input type="text" name="password" placeholder="Enter admin">
            
            <input type="submit" value="Submit">
        </form>
    </div>
</body>
</html>
3.2.2 登录逻辑:login1.php

**核心功能:**​ 验证身份并写入 Session。

复制代码
<?php
/**
 * 登录认证模块
 * 功能:验证用户名密码,建立会话
 */

// 必须开启 Session,这是后续所有防御的基础
session_start();

// 接收 POST 数据
$usr = $_POST['username'] ?? '';
$pwd = $_POST['password'] ?? '';

// 硬编码验证(仅用于实验,生产环境严禁明文存储)
if ($usr === 'admin' && $pwd === 'admin') {
    echo '<h1>Login Success!</h1>';
    
    // 写入管理员权限标识
    $_SESSION["admin"] = 1;
    
    // 调试信息:显示 Session 内容
    echo '<pre>';
    var_dump($_SESSION);
    echo '</pre>';
    
    // 实际应用中这里会有跳转 header("Location: manage.php");
} else {
    echo '<h1>Login Failed!</h1>';
    die(); // 终止脚本
}
?>
3.2.3 带防御的管理页面:manage-defense.php

**核心功能:**​ 生成 Token,渲染表单。

复制代码
<?php
/**
 * 密码修改页面(防御版)
 * 功能:生成 CSRF Token 并展示表单
 */

session_start();

// 1. 权限校验:未登录用户直接拦截
if (!isset($_SESSION["admin"]) || $_SESSION["admin"] != 1) {
    die('<h1 style="color:red;">403 Forbidden: You are not adminer!!</h1>');
}

/*
 * 2. 生成 CSRF Token
 * mt_rand(): 生成随机数
 * time(): 时间戳,增加时效性
 * "Impossible": 盐值(Salt),增加破解难度
 * sha1(): 散列算法,统一长度
 */
$csrf_token = sha1(mt_rand() . time() . "Impossible");

// 3. 将 Token 存入 Session(服务端保存)
$_SESSION["csrf_token"] = $csrf_token;

?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Manage Password - Defense Mode</title>
</head>
<body>
    <h2>Change Administrator Password</h2>
    
    <!-- 表单提交到 add-pass.php -->
    <form action="add-pass.php" method="GET">
        New password:<br>
        <input type="text" name="password_new"><br><br>
        
        Confirm new password:<br>
        <input type="text" name="password_conf"><br><br>
        
        <!-- 
            4. 隐藏字段:Token 随表单提交
            浏览器会自动发送此参数,用户无感知
        -->
        <input name="csrf_token" value="<?php echo htmlspecialchars($csrf_token); ?>" type="hidden">
        
        <input type="submit" value="Submit">
    </form>
</body>
</html>
3.2.4 密码处理与校验:add-pass.php

**核心功能:**​ 校验 Token,执行业务逻辑。

复制代码
<?php
/**
 * 密码修改处理逻辑(防御版)
 * 功能:校验 CSRF Token,修改密码
 */

session_start();

// 1. 权限校验
if (!isset($_SESSION["admin"]) || $_SESSION["admin"] != 1) {
    die('You are not adminer!');
}

// 2. 获取提交的 Token
$csrf_token = $_GET['csrf_token'] ?? '';

/*
 * 3. Token 校验(核心防御逻辑)
 * 条件1:Session 中存在 Token(防止未生成直接提交)
 * 条件2:提交的 Token 必须等于 Session 中的 Token
 */
if (!isset($_SESSION["csrf_token"]) || $_SESSION["csrf_token"] !== $csrf_token) {
    // 调试输出,帮助理解攻击拦截
    echo "Submitted Token: " . $csrf_token . "<br>";
    echo "Session Token: ";
    var_dump($_SESSION["csrf_token"]);
    die('<h3 style="color:red;">Don\'t csrf attack!!</h3>');
}

// 4. 业务逻辑:密码一致性校验
$password_new = $_GET['password_new'] ?? '';
$password_conf = $_GET['password_conf'] ?? '';

if ($password_new === $password_conf) {
    echo '<h1 style="color:green;">pwd changed ok</h1>';
    
    // 模拟写入数据库(此处写入文件代替)
    $log = fopen("password.txt", "a");
    fwrite($log, "Time: " . date("Y-m-d H:i:s") . " | New PWD: " . $password_new . "\n");
    fclose($log);
} else {
    echo "<h3>PWDs doesn't match!!</h3>";
}

/*
 * 5. 销毁 Token(一次性机制)
 * 这是防止重放攻击的关键步骤
 */
unset($_SESSION["csrf_token"]);

echo "<p>Token has been destroyed.</p>";
?>

第四章 实验过程与攻击复现

4.1 正常业务流程验证

  1. **启动环境:**​ 打开 PHPStudy,启动 Apache。

  2. 访问登录页:http://localhost/csrf/login.html

  3. 登录: ​ 输入 admin/admin

  4. 访问管理页:http://localhost/csrf/manage-defense.php

  5. 抓包分析: ​ 使用 F12 或 Burp Suite 查看请求,可以看到 URL 中包含 csrf_token=xxxx

  6. **修改密码:**​ 输入新密码,提交。

  7. 验证结果: ​ 检查 password.txt,确认密码已更新。

4.2 攻击场景模拟:Burp Suite 重放攻击

这是验证防御是否有效的关键环节。

步骤 1:配置代理

在 Firefox 中设置代理为 127.0.0.1:8080,打开 Burp Suite 拦截。

步骤 2:捕获合法请求

完成一次正常的密码修改,Burp Suite 拦截到的 GET 请求如下:

php 复制代码
GET /csrf/add-pass.php?password_new=123456&password_conf=123456&csrf_token=a1b2c3d4e5f6... HTTP/1.1
Host: localhost
Cookie: PHPSESSID=xxxxxxxxxxxxxxxx
步骤 3:构造重放攻击

将请求发送到 Repeater​ 模块。此时,我们模拟攻击者拿到了这个链接(通过 Referer 泄露或 XSS 读取)。

攻击尝试: ​ 直接点击 Send​ 再次发送。

步骤 4:观察结果

第一次发送: ​ 成功,显示 pwd changed ok

**第二次发送:**​ 失败!

页面返回:

复制代码
Don't csrf attack!!

**原因分析:**​

第一次请求成功后,add-pass.php执行了 unset($_SESSION["csrf_token"])

当第二次请求到达服务器时,$_SESSION["csrf_token"]已经不存在了,导致校验失败。

第五章 源码审计与深度对比分析

5.1 与 DVWA 靶场的源码级对比

DVWA 是 Web 安全学习的标杆。我们将本实验与 DVWA 的 CSRF 模块进行深度对比,以提升代码审计能力。

5.1.1 DVWA Low & Medium 级别(无 Token 防御)

Low 源码:

复制代码
if( isset( $_GET[ 'Change' ] ) ) {
    $pass_new = $_GET[ 'password_new' ];
    $pass_conf = $_GET[ 'password_conf' ];
    if( $pass_new == $pass_conf ) {
        // 直接更新数据库
    }
}

分析:无任何防护,典型的 CSRF 漏洞。

Medium 源码:

复制代码
if( isset( $_GET[ 'Change' ] ) ) {
    // 检查 Referer
    if( stripos( $_SERVER[ 'HTTP_REFERER' ], $_SERVER[ 'SERVER_NAME' ] ) !== false ) {
        // 更新密码
    }
}

分析:依赖 Referer 校验。绕过方法:将文件名命名为 localhost.html或利用 https://attacker.com?localhost=xxx

5.1.2 DVWA High 级别(Token 防御)
复制代码
// 生成 Token
if (isset($_SESSION['session_token'])) {
    $session_token = $_SESSION['session_token'];
} else {
    $session_token = bin2hex(random_bytes(16));
    $_SESSION['session_token'] = $session_token;
}

// 校验 Token
if ($_POST['session_token'] !== $_SESSION['session_token']) {
    die("CSRF token invalid.");
}

对比分析:

  1. 随机性: ​ DVWA 使用 random_bytes(16)(强加密随机),本实验使用 mt_rand()(伪随机)。在生产环境中,建议使用 random_int()openssl_random_pseudo_bytes()

  2. **存储:**​ 两者均使用 Session 存储。

  3. 销毁: ​ DVWA 通常每次刷新页面重新生成 Token,本实验采用使用一次即销毁,安全性更高,但用户体验稍差(需刷新页面才能再次提交)。

5.2 防御机制的局限性与改进

虽然本实验的 Token 机制能有效防御 CSRF,但在企业级开发中还需考虑:

  1. Token 绑定用户: ​ 目前的 Token 只存了值,未绑定用户 ID。若 Session 固定,理论上 A 用户的 Token 可能被 B 用户使用(虽然极难)。改进:存储 $_SESSION['csrf_token'][$user_id]

  2. **Token 过期时间:**​ 未设置过期时间。若页面打开很久才提交,Token 可能失效。改进:增加时间戳校验。

  3. HttpOnly Cookie: ​ 配合 HttpOnly标志,防止 XSS 窃取 Cookie。

  4. SameSite Cookie: ​ 现代浏览器支持 Set-Cookie: SameSite=Strict。这能从浏览器层面阻止第三方站点发送 Cookie,是 CSRF 的强力补充防线。


第六章 企业级 CSRF 防御体系设计

在现代复杂的分布式系统中,单一的 Token 防御已不足够。我们需要构建纵深防御体系

6.1 分层防御模型

层级 防御手段 作用
网络层 HTTPS 防止中间人攻击篡改请求
协议层 SameSite Cookie 禁止第三方上下文携带 Cookie
网关层 WAF / API Gateway 过滤异常 Referer 和重复请求
应用层 CSRF Token 验证请求来源合法性
业务层 二次认证 (Re-authentication) 修改密码、转账需再次输入原密码

6.2 前后端分离架构下的 Token 处理

在 React/Vue + RESTful API 架构中,表单提交不再是同步跳转,而是 Ajax 请求。

前端实现:

复制代码
// 从 Meta 标签或 Cookie 中获取 Token
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

fetch('/api/change-password', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken // 放在 Header 中
    },
    body: JSON.stringify({ password: 'newPass' }),
    credentials: 'include' // 必须携带 Cookie
});

后端校验:

复制代码
// Laravel / Spring Security 等框架内置了此类逻辑
if ($request->header('X-CSRF-Token') !== Session::token()) {
    abort(403, 'Invalid CSRF Token');
}

第七章 实验总结与展望

7.1 实验总结

  1. **理论奠基:**​ 从 HTTP 无状态特性出发,阐明了 CSRF 漏洞的根本成因在于浏览器的隐式信任机制。

  2. **实战构建:**​ 使用原生 PHP 搭建了一个包含登录、Session 管理、Token 生成与校验的完整靶场。

  3. **攻击验证:**​ 利用 Burp Suite 成功复现了 CSRF 攻击,并验证了 Token 一次性销毁机制的有效性。

  4. **源码对标:**​ 通过与 DVWA 靶场的源码对比,指出了不同防御级别的实现差异与安全强度。

  5. **体系升华:**​ 提出了适应现代 Web 架构的企业级纵深防御方案。

实验结果证明:基于 Session 的一次性 Token 是目前防御 CSRF 最有效的手段之一,能够有效阻断攻击者利用用户身份执行非授权操作。

7.2 不足与展望

尽管本实验覆盖了 CSRF 的核心防御,但仍有一些方向值得进一步探索:

  1. **JWT 与 CSRF:**​ 在 Token 认证(如 JWT)普及的今天,由于不再依赖 Cookie,CSRF 风险降低,但若将 JWT 存储在 Cookie 中,仍需防范 CSRF。

  2. **双重提交 Cookie:**​ 一种不需要 Session 存储的 Token 方案,利用 Cookie 可读写的特性进行校验。

  3. **自动化扫描:**​ 研究如何利用 Burp Suite 插件或 ZAP 自动化检测 CSRF 漏洞。

7.3 结语

网络安全是一场永无止境的攻防博弈。作为开发人员,我们不能仅仅满足于"功能实现",更应关注"安全实现"。CSRF 防御看似只是一个小小的 Token,实则是对 Web 信任边界的一次深刻重构。希望本报告能为读者构建安全的 Web 应用提供坚实的理论基础与实践指引。


参考文献

  1. OWASP Foundation. (2021). OWASP Top 10:2021.

  2. OWASP. (2023). Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet.

  3. Stuttard, D., & Pinto, M. (2011). The Web Application Hacker's Handbook: Finding and Exploiting Security Flaws. Wiley.

  4. PHP Manual. (2024). Session Security . https://www.php.net/manual/en/session.security.php

  5. MDN Web Docs. (2024). SameSite cookies. Mozilla.


(全文完)

相关推荐
mldong7 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排7 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60618 小时前
如何自己造一个时间处理库
前端·javascript·github
三8448 小时前
redis防御加固与安全基线
数据库·redis·安全·应急响应·防御
走到天涯海角9 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!9 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰9 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
乐迪信息9 小时前
如何通过AI防爆摄像机精准判断船舶超速?
大数据·人工智能·算法·安全·目标跟踪
luiyarch10 小时前
汽车电子ISO 26262功能安全系列(第35期):配置管理与变更管理——给安全系统装上“时空穿梭机”
安全·汽车
mayaairi11 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js