网络空间安全深度实战: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.


(全文完)

相关推荐
冰暮流星1 小时前
javascript之history对象介绍
前端·笔记
IT_陈寒2 小时前
Vite热更新失灵?你可能漏了这个配置
前端·人工智能·后端
丷丩2 小时前
MapLibre GL JS第19课:实时更新要素
前端·javascript·gis·map·mapbox·maplibre gl js
Mr.Daozhi2 小时前
RAG 进阶实战:跑通 Demo 后我连续翻了 6 次车,逐一修复才真正可用(含 Gradio Web 版)
前端·数据库·langchain·大模型·gradio·rag·科研工具
哆来A梦没有口袋2 小时前
干货精讲 | 初级CSS面试高频考题
前端·css·面试
掘金012 小时前
EmbedPDF Vue 版 完整正文文档 全网首发
前端
OpenTiny社区2 小时前
操作ArkTS页面跳转及路由相关心得
前端·typescript·web·opentiny
xiaohua0708day2 小时前
Lodash库
前端·javascript·vue.js
huakoh2 小时前
Claude Code 从零到上手指南:国产工具链复现80% Agent能力,DeepSeek+LangChain实战
前端