1. 项目概述:从“小白”视角理解DVWA与CSRF
如果你刚接触网络安全,听到“DVWA”、“CSRF”这些词可能有点发怵,觉得是高手才玩的东西。别担心,我刚开始也这样。今天咱们就从一个最基础、最经典的靶场环境——DVWA(Damn Vulnerable Web Application)的Low安全级别开始,手把手、掰开揉碎了讲清楚什么是CSRF攻击,以及如何亲手复现它。这不是一篇高高在上的理论文章,而是一个老手带着你,在本地环境里一步步操作、观察、思考的实战记录。
DVWA本质上是一个故意设计了很多安全漏洞的PHP/MySQL网站,专门用来给我们这些安全爱好者和学习者“练手”的。它把漏洞难度分成了四个等级:Low, Medium, High, Impossible。Low级别意味着几乎没有任何防护,漏洞原理以最赤裸、最原始的方式呈现,非常适合我们理解漏洞的本质。而CSRF,全称Cross-Site Request Forgery,中文叫“跨站请求伪造”。这个名字听起来复杂,但核心思想很简单: 攻击者欺骗用户的浏览器,去发送一个用户本意并不想发送的请求 。举个例子,你登录了网上银行(A网站),然后不小心点了一个恶意链接(B网站),这个恶意链接里暗藏了一个向银行发起转账的请求。因为你的浏览器还保存着登录银行的凭证(比如Cookie),银行收到这个从你浏览器发来的请求,会以为是你自己操作的,从而完成转账。整个过程,你作为用户可能完全不知情。
所以,我们今天的目标非常明确:在DVWA靶场的Low安全级别下,找到那个存在CSRF漏洞的功能点(通常是修改密码),然后我们扮演“攻击者”,构造一个恶意页面,让“受害者”(其实就是我们自己开的另一个浏览器标签)在不知情的情况下,把自己的密码给改了。通过这个完整的实操,你会深刻理解CSRF的攻击流程、危害以及最基础的防御思路。这不仅是通关一个靶场,更是为你后续理解更复杂的Web安全漏洞打下坚实的基础。
2. 环境准备与靶场搭建要点
工欲善其事,必先利其器。在开始“攻击”之前,我们得先把靶场立起来。虽然网上有很多一键安装包或者Docker镜像,但我强烈建议初学者从最“原始”的方式开始,哪怕多花点时间,这个过程能帮你理解一个Web应用运行的基本环境,这对后续学习至关重要。
2.1 基础运行环境搭建
DVWA是一个PHP+MySQL的应用,所以我们需要三样东西:Web服务器(如Apache)、PHP解释器和MySQL数据库。对于Windows用户,最省心的方案是使用集成环境包,比如 XAMPP 或 PHPStudy 。我以XAMPP为例,因为它跨平台且配置简单。
首先,去Apache Friends官网下载XAMPP并安装。安装路径最好没有中文和空格,比如
C:\xampp
。安装完成后,打开XAMPP控制面板,启动
Apache
和
MySQL
这两个服务。看到它们旁边亮起绿灯,就说明服务运行正常了。此时,你在浏览器访问
http://localhost
,应该能看到XAMPP的欢迎页面。
接下来,下载DVWA。你可以从GitHub上搜索“DVWA”找到官方仓库,下载ZIP包。将解压后的整个文件夹(通常叫
dvwa
或
DVWA-master
)复制到XAMPP的网站根目录下。这个根目录通常是
C:\xampp\htdocs\
。复制完后,你访问
http://localhost/dvwa
就能看到DVWA的安装界面了。
注意:很多新手在这一步会遇到文件权限问题。确保你的
htdocs目录以及其下的dvwa目录有足够的读写权限。如果遇到配置文件无法创建的错误,可以尝试右键文件夹->属性->安全,给当前用户添加“完全控制”权限(仅用于本地学习环境)。
2.2 数据库与配置文件调整
访问
http://localhost/dvwa/setup.php
页面,你会看到DVWA的安装检查页面。这里会列出所有需要满足的条件。最常见的问题是以下两个:
-
PHP函数 allow_url_include 禁用
:DVWA的某些功能(如文件包含)需要这个设置。在XAMPP中,找到
C:\xampp\php\php.ini文件,用记事本打开,搜索allow_url_include,将其值从Off改为On。改完后 必须重启Apache服务 才能生效。 -
数据库连接失败
:页面会显示“数据库连接错误”。我们需要配置数据库连接信息。在DVWA目录下,找到
config文件夹,里面有一个config.inc.php.dist文件。复制一份,并重命名为config.inc.php。用编辑器打开这个新文件,找到数据库配置部分:
通常,XAMPP默认的MySQL用户名是$_DVWA[ 'db_server' ] = '127.0.0.1'; $_DVWA[ 'db_database' ] = 'dvwa'; $_DVWA[ 'db_user' ] = 'root'; $_DVWA[ 'db_password' ] = 'p@ssw0rd';root,密码为空。所以你需要把$_DVWA[ 'db_password' ]的值改为''(两个单引号,中间什么都没有)。如果你自己设过MySQL密码,就填你设置的密码。
保存文件后,刷新
setup.php
页面。如果一切顺利,页面底部的状态应该全是绿色的“OK”。然后点击页面上的
“Create / Reset Database”
按钮。这个操作会为你创建一个名为
dvwa
的数据库,并填入初始数据。
2.3 首次登录与安全级别设置
数据库创建成功后,页面会自动跳转到登录页,或者你可以手动访问
http://localhost/dvwa/login.php
。默认的登录账号是
admin
,密码是
password
。成功登录后,你就进入了DVWA的主界面。
在开始漏洞练习前,还有关键一步: 设置安全级别 。在左侧菜单栏找到 “DVWA Security” 选项,点击进入。你会看到一个下拉选择框,里面就有我们提到的四个级别。为了本次实验,我们选择 “Low” ,然后点击“Submit”提交。页面会提示安全级别已更新。这个设置是全局的,意味着所有漏洞模块(SQL注入、XSS、CSRF等)都会处于Low级别的防护状态。
至此,你的本地DVWA靶场就已经完全准备好了。它现在就像一个满是破绽的木人桩,等着你去“击打”。我们接下来的所有操作,都将在这个
http://localhost/dvwa
的环境下进行。
3. CSRF漏洞原理深度拆解
在动手之前,我们必须把CSRF的原理吃透。很多教程只讲步骤,不讲为什么,导致学完还是云里雾里。我会用最生活化的比喻和场景,帮你把这块骨头啃下来。
3.1 核心攻击模型:“借刀杀人”
我们可以把CSRF攻击想象成一场“借刀杀人”的戏码。这里面有三个角色:
- 受害者(Victim) :一个正常用户,他已经登录了某个信任的网站(比如银行网站A),他的浏览器里保存着登录凭证(Session Cookie)。
- 受信任的网站(Trusted Site) :网站A,它认为来自受害者浏览器的请求都是受害者本人发出的。
- 攻击者(Attacker) :他构造了一个恶意网站B,并想方设法诱使受害者去访问它。
攻击流程是这样的:
- 受害者登录了银行网站A,并保持了登录状态。
- 攻击者通过邮件、论坛、聊天软件等渠道,给受害者发送了一个链接,这个链接指向攻击者控制的恶意网站B。
- 受害者点击了这个链接,浏览器打开了网站B。
- 网站B的页面中,隐藏着一个自动提交的表单(或一个图片、脚本标签),这个表单的提交目标(action)指向的是银行网站A的某个敏感操作接口,比如“转账给攻击者”。
- 受害者的浏览器在加载网站B时,会自动向银行网站A发送这个转账请求。由于受害者之前登录过A,浏览器会 自动携带 上属于A网站的Cookie。
- 银行网站A收到了这个请求,它检查Cookie,发现是合法的登录会话,于是便执行了转账操作。
整个过程中,受害者只是在不知情的情况下访问了一个网页(B),而攻击者利用受害者浏览器对网站A的“信任关系”,完成了恶意操作。这就是“跨站请求伪造”——请求是从受害者浏览器跨到A站的,但却是被B站伪造的。
3.2 与XSS的本质区别
初学者常常混淆CSRF和XSS(跨站脚本攻击)。它们虽然都带“跨站”,但本质截然不同:
- XSS :核心是“脚本注入”。攻击者向网站A注入恶意脚本,当其他用户浏览网站A时,脚本在其浏览器中执行, 窃取该用户的数据(如Cookie)或冒充该用户 。攻击目标是网站A的用户,漏洞存在于网站A的代码中。
- CSRF :核心是“请求伪造”。攻击者自己有一个网站B,他利用用户浏览器对网站A的信任, 代替用户向网站A发送请求 。攻击目标是网站A本身(的某个功能),利用的是网站A缺乏对请求来源验证的漏洞。
用一个简单类比:XSS是骗子混进了你家小区(网站A),冒充物业骗走了你的门禁卡(Cookie)。CSRF是骗子在小区外给你打个电话(网站B),骗你用自己的门禁卡(浏览器自动带Cookie)从里面把小区大门给他打开了。
3.3 DVWA Low级CSRF漏洞场景分析
在DVWA中,CSRF漏洞模块模拟了一个“修改密码”的功能。在Low安全级别下,它的代码大概是这样的(原理示意):
if( isset( $_GET[ 'Change' ] ) ) {
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// 检查两次输入是否一致
if( $pass_new == $pass_conf ) {
// 直接修改数据库中对应用户的密码
$update = "UPDATE `users` SET password = '" . md5( $pass_new ) . "' WHERE user = '" . dvwaCurrentUser() . "';";
// 执行SQL...
echo "Password Changed.";
}
}
这段代码存在一个致命的CSRF漏洞: 它只验证了两次密码输入是否一致,以及当前用户的会话是否有效,但完全没有验证这个“修改密码”的请求到底是不是来自本网站自己的页面 。
也就是说,只要用户已经登录了DVWA,他的浏览器在访问任何其他网页时,如果那个网页包含了一个指向
http://localhost/dvwa/vulnerabilities/csrf/?password_new=hack&password_conf=hack&Change=Change
的请求(比如一个自动加载的图片标签 ``),浏览器就会自动带着用户的DVWA会话Cookie去访问这个链接,从而在用户不知情的情况下,把他的密码修改成
hack
。
理解了这一点,我们构造攻击的思路就非常清晰了:我们需要创建一个独立的HTML页面,这个页面能自动或诱导用户向DVWA的密码修改接口发送一个GET请求。
4. 实战:构造并实施CSRF攻击
理论已经足够,现在让我们打开DVWA,切换到CSRF模块,开始真正的实战。请跟着我的步骤一步一步来,我会解释每一个操作的意图。
4.1 信息收集与漏洞点定位
首先,登录DVWA,在左侧菜单选择 “CSRF”。你会看到一个非常简单的修改密码表单,需要输入新密码和确认密码。
第一步,观察请求方式。
输入一个新密码(比如
test123
)并点击“Change”按钮。此时,不要只看页面变化,一定要打开浏览器的
开发者工具
(按F12)。切换到 “Network”(网络)标签页,清空记录,然后再次点击“Change”按钮。你会看到一条新的网络请求记录。
点击这条记录,查看“Headers”(标头)。重点看:
-
Request URL
:它应该是类似
http://localhost/dvwa/vulnerabilities/csrf/?password_new=test123&password_conf=test123&Change=Change的形式。这说明修改密码的操作是通过 GET请求 完成的,所有参数(新密码、确认密码、动作)都直接暴露在了URL里。这对于CSRF攻击来说是“最友好”的形式。 -
Cookie
:在请求头里,你会看到
Cookie: PHPSESSID=xxx; security=low。这就是你的会话凭证,浏览器会自动在每次向localhost/dvwa发请求时带上它。
第二步,分析漏洞利用条件。 从上面的信息我们可以得出,要伪造这个请求,我们需要:
- 让受害者浏览器访问一个特定构造的URL。
- 确保受害者已经登录DVWA(即浏览器中有有效的PHPSESSID Cookie)。
- 受害者访问这个URL时,密码就会被修改。
因为这是GET请求,构造攻击变得极其简单。我们甚至不需要复杂的HTML表单,只需要一个能让浏览器发起GET请求的标签即可,比如
、
,或者直接是一个链接。
4.2 构造恶意攻击页面
我们在本地创建一个简单的HTML文件来模拟攻击者的恶意网站。在你的电脑任意位置(比如桌面),新建一个文本文档,命名为
csrf_attack.html
。用记事本或代码编辑器打开它,输入以下内容:
<!DOCTYPE html>
<html>
<head>
<title>看起来人畜无害的页面</title>
</head>
<body>
<h1>恭喜你抽中大奖!</h1>
<p>点击下方链接领取你的奖励:</p>
<!-- 方式1:直接诱导点击的链接 -->
<a href="http://localhost/dvwa/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change" target="_blank">
点击领取百万红包
</a>
<br><br>
<p>或者,这个页面加载时,你的密码可能已经被悄悄修改了...</p>
<!-- 方式2:自动加载的图片(最隐蔽的GET请求) -->
<img src="http://localhost/dvwa/vulnerabilities/csrf/?password_new=auto_hacked&password_conf=auto_hacked&Change=Change" width="0" height="0" />
<script>
// 方式3:使用JavaScript自动跳转(兼容性更好)
// setTimeout(function() {
// window.location.href = 'http://localhost/dvwa/vulnerabilities/csrf/?password_new=js_hacked&password_conf=js_hacked&Change=Change';
// }, 3000);
// 注释掉的方式3表示3秒后自动跳转攻击
</script>
</body>
</html>
我来解释一下这个页面的几种攻击方式:
- 方式一(链接) :最直白。攻击者诱骗用户点击一个伪装成奖励的链接。用户点击后,会打开一个新的标签页,访问那个构造好的DVWA密码修改URL。因为新标签页继承了主标签页的Cookie,所以请求会成功,页面会显示“Password Changed.”。这种方式需要用户交互。
-
方式二(图片)
:非常隐蔽。`` 标签的
src属性可以是一个URL。当浏览器加载这个HTML页面时,它会自动去请求src指定的图片资源。我们把图片的URL设置成攻击URL,浏览器就会自动发起一个GET请求。我们将图片宽高设为0,使其在页面上不可见。用户只是访问了这个恶意页面,什么也没点,攻击就已经完成了。 - 方式三(JS跳转) :同样隐蔽。通过JavaScript设置一个延时跳转,用户可能在阅读页面内容时,突然页面就跳转了,攻击也随之完成。
为了演示最隐蔽的攻击,我们暂时启用方式二,将方式一的链接和方式三的脚本先注释掉(或者删除)。保存
csrf_attack.html
文件。
4.3 模拟攻击过程与结果验证
现在,我们开始模拟攻击场景。你需要打开两个浏览器窗口或标签页,我们分别称之为 “受害者窗口” 和 “攻击者窗口” 。
-
受害者窗口
:打开浏览器,访问
http://localhost/dvwa/login.php,用 admin/password 登录。确保你能正常访问各个模块。 这个窗口代表已经登录了信任网站(DVWA)的普通用户 。记住你现在的密码是password。 -
攻击者窗口
:在这个窗口里,我们直接双击打开刚才创建的
csrf_attack.html文件。或者,如果你把它放到了Web服务器目录下(比如htdocs),也可以通过http://localhost/csrf_attack.html访问。 这个窗口代表用户被诱骗访问的恶意网站 。
关键的一步来了:在
攻击者窗口
打开
csrf_attack.html
后,页面看起来可能只有一行文字(“或者,这个页面加载时...”),因为图片被隐藏了。但攻击已经在后台发生了。
现在,切换回
受害者窗口
。你不需要做任何操作。直接点击DVWA左侧菜单的 “CSRF” 模块,再次进入修改密码页面。
注意观察当前密码输入框下方的文字提示
。或者,更直接的方法是,点击“Logout”退出,然后尝试用旧的密码
password
重新登录。你会发现,登录失败了!
再用我们攻击中设置的新密码
auto_hacked
尝试登录。登录成功!这就证明,CSRF攻击成功了。受害者在完全不知情、没有进行任何主动修改密码操作的情况下,仅仅因为访问了一个恶意页面,他的密码就被篡改了。
实操心得:在实际测试中,浏览器的同源策略(Same-Origin Policy)有时会阻止页面加载“不同源”的图片(因为我们的恶意页面是
file://协议或localhost的不同路径),导致图片请求失败。如果方式二没成功,可以尝试方式一(诱导点击)或方式三(JS跳转)。更可靠的方法是将恶意页面部署到另一个域名或端口的Web服务器上(例如http://127.0.0.1:8080/attack.html),这更符合真实的跨站攻击场景。你可以用Python快速启动一个简易HTTP服务器:在恶意HTML文件所在目录打开命令行,运行python -m http.server 8080,然后通过http://127.0.0.1:8080/csrf_attack.html访问。
5. 漏洞根源分析与中级(Medium)级别攻防
成功复现了Low级别的攻击,我们不仅要知其然,更要知其所以然,并且看看当防御措施稍微升级后,攻击方式该如何调整。
5.1 Low级别漏洞代码审计
让我们回头看Low级别的后端代码(通常位于
dvwa/vulnerabilities/csrf/source/low.php
)。其核心逻辑简化后如下:
if( isset( $_GET[ 'Change' ] ) ) {
// 获取参数
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// 唯一检查:密码是否一致
if( $pass_new == $pass_conf ) {
// 直接更新数据库
$pass_new = md5( $pass_new );
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$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>' );
// 反馈结果
echo "<pre>Password Changed.</pre>";
} else {
echo "<pre>Passwords did not match.</pre>";
}
}
漏洞根因一目了然: 服务器端只验证了业务逻辑(密码一致性)和用户会话有效性,但没有对请求的来源(Origin/Referer)进行任何验证 。它无法区分这个“修改密码”的请求是来自DVWA网站自身的表单提交,还是来自外部恶意页面伪造的请求。
5.2 Medium级别防御机制与绕过
在DVWA Security中将级别调整为“Medium”,然后刷新CSRF页面。你会发现页面看起来没什么变化。但后端代码(
medium.php
)已经增加了防御:
if( isset( $_GET[ 'Change' ] ) ) {
// 检查Referer头是否包含服务器主机名(例如 ‘localhost’)
if( stripos( $_SERVER[ 'HTTP_REFERER' ] , $_SERVER[ 'SERVER_NAME' ] ) !== false ) {
// 原有的修改密码逻辑...
} else {
// 请求来源不合法
echo "<pre>That request didn't look correct.</pre>";
}
}
这段代码尝试检查HTTP请求头中的
Referer
字段。
Referer
头会告诉服务器,当前这个请求是从哪个页面链接过来的。代码检查
Referer
值中是否包含服务器自己的名称(
SERVER_NAME
,如’localhost’)。如果包含,则认为请求是合法的;否则,拒绝请求。
这是一种常见但 并不安全 的CSRF防御手段。攻击者可以通过多种方式绕过:
-
空Referer或缺失Referer
:在某些情况下,浏览器可能不会发送Referer头,比如用户直接从地址栏输入URL、从书签打开、或使用某些隐私模式/浏览器扩展。如果服务器配置不当,将空Referer视为合法,则攻击依然可行。不过DVWA的Medium级别代码使用
stripos()检查,空值会导致检查失败。 -
攻击者控制子域名或路径
:如果检查逻辑不严谨(比如DVWA这里只是检查“包含”localhost),那么攻击者如果拥有
attacker.localhost这样的子域名,或者能上传文件到目标服务器的某个路径下,就可以通过该页面发起攻击。 -
利用Referer过滤漏洞
:更常见和通用的绕过方法是,
攻击者可以创建一个HTML页面,然后通过
data:URL 或javascript:伪协议来触发请求 ,这些方式发起的请求,其Referer头可能是空、是about:blank或者是当前页面的源,从而可能绕过检查。但这种方法受浏览器安全策略影响较大。
对于DVWA的Medium级别,一个更直接的绕过方法是:
因为检查的是Referer中是否包含“localhost”,而我们的恶意页面本身就是放在本地(
file://
协议或
localhost:8080
),其Referer可能本身就包含
localhost
字符串
。让我们测试一下。
将之前
csrf_attack.html
中的攻击URL稍作修改,并确保通过本地Web服务器(如
http://127.0.0.1:8080/
)访问它。然后重复攻击步骤。你会发现,攻击可能依然成功!因为从
http://127.0.0.1:8080/attack.html
发往
http://localhost/dvwa/...
的请求,其Referer头是
http://127.0.0.1:8080/attack.html
,其中并不包含字符串
localhost
(它是
127.0.0.1
)。但如果我们用
http://localhost:8080
来访问恶意页面呢?Referer就会变成
http://localhost:8080/attack.html
,其中包含了
localhost
,这样就绕过了检查。
注意事项:Referer检查是一种不可靠的防御。首先,Referer头可以被用户浏览器设置或某些安全软件禁用。其次,如上所述,逻辑容易被绕过。最后,它可能泄露用户隐私(用户从哪个页面跳转而来)。因此, 绝对不要依赖Referer头作为唯一的CSRF防御手段 。
6. 高级(High)级别防御与安全开发实践
将DVWA安全级别调到“High”,CSRF模块的防御机制发生了质的变化。页面源代码中,多出了一个隐藏的输入域(input)。
<input type="hidden" name="user_token" value="a1b2c3d4e5f6g7h8i9j0...">
这是 Anti-CSRF Token(反CSRF令牌) ,也是目前业界防御CSRF最有效、最通用的方案。
6.1 Token防御机制原理详解
其工作原理可以概括为“一次一密”:
-
生成与绑定
:当用户访问包含表单的页面(如修改密码页)时,服务器端会生成一个随机、不可预测的字符串(Token),将其存放在两个地方:a) 服务器端的用户会话(Session)中;b) 返回给客户端的表单里,作为一个隐藏字段(如上面的
user_token)。 - 提交与验证 :当用户提交表单时,浏览器会把这个隐藏的Token值随着其他表单数据一起提交给服务器。
- 校验与销毁 :服务器收到请求后,会从请求中取出Token,并与当前用户会话中存储的Token进行比对。如果两者一致且未过期,则认为请求是合法的,来自本网站自己的页面。随后,服务器通常会 使当前Token失效 ,并为下一次表单提交生成一个新的Token。
为什么这种方式能有效防御CSRF?
- 不可预测性 :Token是随机的,攻击者无法提前猜出。
- 同源限制 :由于浏览器的同源策略(SOP),恶意网站B无法通过JavaScript读取网站A页面中的Token值(除非网站A存在XSS漏洞)。
- 会话绑定 :Token与特定用户的当前会话绑定,不同用户、同一用户不同会话的Token都不同。
因此,即使攻击者伪造了请求的URL和参数,他也无法伪造正确的Token值。服务器校验失败,请求就会被拒绝。
6.2 针对Token机制的潜在攻击思路
没有任何安全方案是绝对完美的,Token机制也有其潜在的攻击面:
- Token泄露 :如果网站同时存在XSS漏洞,攻击者可以利用XSS窃取页面中的Token,然后结合CSRF发动攻击。这属于组合漏洞。
- Token生成或校验逻辑缺陷 :如果Token生成算法可预测(如基于时间戳的简单哈希),或者校验逻辑存在缺陷(如只检查Token是否存在而不校验其值),则可能被绕过。
- 会话固定(Session Fixation)攻击 :如果Token与会话绑定,而会话ID本身可以被攻击者预设,那么他可能间接控制Token。
对于DVWA High级别,除非你能先找到并利用一个XSS漏洞来获取Token,否则单纯的CSRF攻击是无法成功的。这体现了安全中的一个重要原则: 防御需要多层次、纵深部署 。
6.3 企业级安全开发建议
在实际开发中,我们应该如何实施CSRF防护呢?
- 首选:使用成熟的框架内置功能 :如果你在使用Spring Security(Java)、Django(Python)、Laravel(PHP)、ASP.NET Core(C#)等主流Web框架,它们都提供了开箱即用的CSRF防护中间件。 强烈建议直接使用,不要自己造轮子 。这些实现经过了广泛测试和验证。
-
自定义实现Token
:如果框架不支持,需要自行实现,请确保:
- Token足够随机 :使用密码学安全的随机数生成器(CSPRNG)。
- 绑定会话与请求 :Token应与当前用户会话ID强关联。
- 每次表单或关键操作都使用新Token :重要操作(如登录、支付、修改密码)应使用一次性的Token。
- 安全地传递和存储 :通过隐藏表单字段传递,避免出现在URL中(防止被日志记录)。在服务器端,将其存储在Session中。
- 校验后立即失效 :对于敏感操作,校验成功后应立即使当前Token失效。
-
补充防御:SameSite Cookie属性
:为Cookie设置
SameSite属性。SameSite=Strict或SameSite=Lax可以阻止浏览器在跨站请求中发送特定的Cookie,这从根源上削弱了CSRF攻击的基础。但这不能完全替代Token,应作为深度防御的一环。 - 关键操作使用二次验证 :对于极其敏感的操作(如大额转账、修改核心账户信息),除了CSRF Token,应强制要求用户进行二次验证,如输入手机验证码、密码、使用U盾等。这不仅是防御CSRF,更是提升整体账户安全。
7. 拓展:自动化工具与漏洞挖掘思路
手动构造攻击页面有助于理解原理,但在实际安全测试或漏洞挖掘中,我们更需要高效的工具和方法。
7.1 使用Burp Suite生成CSRF PoC
Burp Suite是Web安全测试的瑞士军刀。它的 “Generate CSRF PoC” 功能可以一键生成攻击测试页面。
- 在DVWA Low级别下,拦截一个正常的“修改密码”的GET请求(使用Burp的Proxy拦截功能)。
- 在Burp的Proxy历史记录或Repeater模块中,找到这条请求。
- 右键点击该请求,选择 “Engagement tools” -> “Generate CSRF PoC” 。
- Burp会弹出一个窗口,里面已经自动生成了包含该请求的HTML代码。你可以直接复制这段代码,保存为HTML文件。
- 生成的PoC通常是一个包含隐藏表单并支持自动提交的页面,比我们手动写的更完善和隐蔽。
这个功能在测试Medium级别需要检查Referer的场景时尤其有用,你可以快速生成不同形式的PoC进行测试。
7.2 漏洞挖掘与测试方法论
在真实的黑盒或灰盒测试中,如何系统地寻找CSRF漏洞?
- 目标识别 :首先梳理应用的所有 状态变更操作 。关注:用户资料修改(邮箱、密码、地址)、资金操作(转账、支付)、内容发布(发帖、评论)、权限变更(授权、添加用户)等。这些是CSRF的高价值目标。
-
请求分析
:使用代理工具(如Burp)捕获这些操作的请求。重点关注:
- 请求方法 :GET请求的CSRF风险最高(如我们所见)。POST请求同样存在风险,但构造稍复杂(需要JavaScript自动提交表单)。
- 参数规律 :请求参数是否简单、有规律?是否存在不可预测的Token或校验参数?
- Cookie依赖 :请求是否仅依赖Cookie进行身份认证?是否还有其他头部(如自定义Header)?
-
Token检测
:检查请求中是否存在名为
csrf_token,authenticity_token,nonce,_token等常见Token字段。观察其值是否随机、每次是否变化。如果存在固定值、空值或可预测的值(如基于时间戳),则可能存在漏洞。 -
Referer/Origin检查
:尝试篡改或删除请求中的
Referer或Origin头,观察服务器响应。如果删除后请求依然成功,说明没有校验;如果返回错误,则说明有校验,需要进一步测试校验逻辑的严谨性(如是否可绕过)。 -
同源策略(SOP)与CORS
:理解目标网站的CORS(跨源资源共享)策略。如果配置不当(如允许任意源
Access-Control-Allow-Origin: *且允许携带凭证Access-Control-Allow-Credentials: true),可能会加剧CSRF风险或催生新的攻击方式。 - 工具辅助扫描 :使用自动化漏洞扫描器(如Burp的Active Scan, OWASP ZAP)可以帮助发现常见的CSRF问题,但绝不能完全依赖。扫描器可能会漏报(尤其是逻辑复杂的校验)或误报,需要人工复核。
7.3 从攻击者视角到防御者视角的转变
通过DVWA的CSRF通关,我们完整地走过了从漏洞发现、原理理解、攻击构造到防御机制分析的整个过程。这不仅仅是学会了一个攻击技巧,更重要的是建立了以下安全思维:
- 不信任原则 :服务器绝不能信任任何来自客户端的请求。每一个可能引发状态变更的请求,都必须经过严格的来源和意图验证。
- 最小权限原则 :即使发生CSRF,也应将损害降到最低。例如,修改密码是否需要旧密码验证?转账是否有每日限额?关键操作是否有操作日志和通知?
- 纵深防御 :不要指望单一措施能解决所有问题。结合使用CSRF Token、SameSite Cookie、关键操作二次验证、敏感操作人工审核等多层防护。
- 安全开发生命周期(SDL) :将安全考虑嵌入到软件开发的每一个阶段(需求、设计、编码、测试、部署),而不是事后补救。在代码审查时,要特别关注所有处理表单提交和状态变更的端点。
最后,我个人在多年的渗透测试和代码审计中发现,很多CSRF漏洞的产生,并不是因为开发者不知道这个漏洞,而是因为**“忘记”**。在快速迭代的业务压力下,一个后台管理功能、一个内部API接口,可能就被遗漏了防护。因此,建立自动化的安全代码检查清单、在框架层面统一防护、以及对开发人员进行持续的安全意识培训,远比事后修复一个具体的漏洞更为重要。DVWA的Low到Impossible级别,正是这种安全意识从无到有、从弱到强的最佳写照。希望你在通关之后,不仅能复现攻击,更能将这种防御思维带入到你未来的开发和测试工作中去。
259

被折叠的 条评论
为什么被折叠?



