全栈实验报告
**适用场景:** 信息安全专业毕业设计 / 顶级高校实验报告 / 企业级安全开发规范
**核心技术:** 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) ,而是欺骗用户的浏览器,利用其已登录的信任状态,在受害者毫不知情的情况下,以受害者的身份执行非预期的操作。例如:
-
**金融领域:** 伪造银行转账请求。
-
**社交工程:** 篡改用户绑定的邮箱或手机号。
-
**基础设施:** 在路由器或 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 实验目标
本实验旨在达成以下目标:
-
**原理复现:** 搭建一个存在 CSRF 风险的 Web 环境。
-
**机制构建:** 手动实现基于 PHP Session 的 Token 生成、分发、校验与销毁全流程。
-
**攻击验证:** 使用 Burp Suite 模拟重放攻击,验证无防御状态下的脆弱性。
-
**防御评估:** 验证 Token 的一次性有效性,分析其抗绕过能力。
-
**源码对标:** 对比 DVWA 靶场源码,提升代码审计能力。
第二章 Web 身份认证与 CSRF 理论基础
2.1 HTTP 协议的无状态性与会话管理
HTTP 协议是无状态的(Stateless)。这意味着服务器默认无法区分两个连续的请求是否来自同一个用户。为了解决这个问题,Web 开发引入了 Session-Cookie 机制:
-
**握手:** 用户登录,服务器验证凭据。
-
创建 Session: 服务端创建一个 Session 对象,存储在内存或数据库中,并生成一个唯一的
Session ID。 -
下发 Cookie: 服务器通过
Set-Cookie响应头,将Session ID发送给浏览器。 -
**自动携带:** 此后,浏览器对该域的所有请求都会自动携带该 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 正常业务流程验证
-
**启动环境:** 打开 PHPStudy,启动 Apache。
-
访问登录页:
http://localhost/csrf/login.html。 -
登录: 输入
admin/admin。 -
访问管理页:
http://localhost/csrf/manage-defense.php。 -
抓包分析: 使用 F12 或 Burp Suite 查看请求,可以看到 URL 中包含
csrf_token=xxxx。 -
**修改密码:** 输入新密码,提交。
-
验证结果: 检查
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."); }
对比分析:
-
随机性: DVWA 使用
random_bytes(16)(强加密随机),本实验使用mt_rand()(伪随机)。在生产环境中,建议使用random_int()或openssl_random_pseudo_bytes()。 -
**存储:** 两者均使用 Session 存储。
-
销毁: DVWA 通常每次刷新页面重新生成 Token,本实验采用使用一次即销毁,安全性更高,但用户体验稍差(需刷新页面才能再次提交)。
5.2 防御机制的局限性与改进
虽然本实验的 Token 机制能有效防御 CSRF,但在企业级开发中还需考虑:
-
Token 绑定用户: 目前的 Token 只存了值,未绑定用户 ID。若 Session 固定,理论上 A 用户的 Token 可能被 B 用户使用(虽然极难)。改进:存储
$_SESSION['csrf_token'][$user_id]。 -
**Token 过期时间:** 未设置过期时间。若页面打开很久才提交,Token 可能失效。改进:增加时间戳校验。
-
HttpOnly Cookie: 配合
HttpOnly标志,防止 XSS 窃取 Cookie。 -
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 实验总结
-
**理论奠基:** 从 HTTP 无状态特性出发,阐明了 CSRF 漏洞的根本成因在于浏览器的隐式信任机制。
-
**实战构建:** 使用原生 PHP 搭建了一个包含登录、Session 管理、Token 生成与校验的完整靶场。
-
**攻击验证:** 利用 Burp Suite 成功复现了 CSRF 攻击,并验证了 Token 一次性销毁机制的有效性。
-
**源码对标:** 通过与 DVWA 靶场的源码对比,指出了不同防御级别的实现差异与安全强度。
-
**体系升华:** 提出了适应现代 Web 架构的企业级纵深防御方案。
实验结果证明:基于 Session 的一次性 Token 是目前防御 CSRF 最有效的手段之一,能够有效阻断攻击者利用用户身份执行非授权操作。
7.2 不足与展望
尽管本实验覆盖了 CSRF 的核心防御,但仍有一些方向值得进一步探索:
-
**JWT 与 CSRF:** 在 Token 认证(如 JWT)普及的今天,由于不再依赖 Cookie,CSRF 风险降低,但若将 JWT 存储在 Cookie 中,仍需防范 CSRF。
-
**双重提交 Cookie:** 一种不需要 Session 存储的 Token 方案,利用 Cookie 可读写的特性进行校验。
-
**自动化扫描:** 研究如何利用 Burp Suite 插件或 ZAP 自动化检测 CSRF 漏洞。
7.3 结语
网络安全是一场永无止境的攻防博弈。作为开发人员,我们不能仅仅满足于"功能实现",更应关注"安全实现"。CSRF 防御看似只是一个小小的 Token,实则是对 Web 信任边界的一次深刻重构。希望本报告能为读者构建安全的 Web 应用提供坚实的理论基础与实践指引。
参考文献
-
OWASP Foundation. (2021). OWASP Top 10:2021.
-
OWASP. (2023). Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet.
-
Stuttard, D., & Pinto, M. (2011). The Web Application Hacker's Handbook: Finding and Exploiting Security Flaws. Wiley.
-
PHP Manual. (2024). Session Security . https://www.php.net/manual/en/session.security.php
-
MDN Web Docs. (2024). SameSite cookies. Mozilla.
(全文完)















