资讯动态

DVWA靶场CSRF实战:从漏洞原理到Token防护的关键路径解析

发布时间:2026/10/13 3:45:42 来源:尧图企业网站定制
做安全测试的人多半绕不开一个基本功搞清楚CSRF跨站请求伪造到底是怎么发生的。我在DVWA靶场里练习CSRF时最大的感受是——这个漏洞看着简单但每一级难度都对应着现实中真实存在过的防护与绕过方式把LOW到HIGH全部走一遍基本上就能把CSRF的原理、利用和防御串成一条完整的链路。这篇文章就记录我在DVWA靶场练习CSRF的完整过程包括环境准备、每一级难度的源码分析、利用思路以及落地到真实项目里的防御建议。适合刚学Web安全的同学也适合想系统梳理CSRF知识点的开发或测试同学。1. CSRF漏洞的核心原理为什么浏览器会帮攻击者发出合法请求1.1 先从最简单的场景说起假设有一个社交网站它有一个修改昵称的功能。你登录以后浏览器里保存着这个网站的会话Cookie。现在你在另一个标签页打开了一个来路不明的页面这个页面里藏着一个隐藏表单表单的提交地址指向修改昵称的接口。你的浏览器在加载这个页面时页面里的脚本自动提交了表单。因为你的浏览器仍然带着目标网站的Cookie服务器收到请求后会认为这个请求来自已登录用户本人于是真的把昵称改了。这就是CSRF的核心服务器无法区分这个请求是用户主动发出的还是被第三方页面伪造的。浏览器把Cookie自动带上本来是为了方便用户免去重复登录但这也让攻击者有了可乘之机。我在DVWA里第一次跑通CSRF利用时才真正意识到原来用户什么都没做只是多开了一个页面密码就能被改掉。1.2 CSRF成立的三要素要触发CSRF通常需要同时满足三个条件目标站点有状态变更类接口比如改密码、转账、修改邮箱、发布内容。只读接口一般没有利用价值。用户的浏览器在访问攻击者页面时仍然处于目标站点的已登录状态且Cookie没有被SameSite等机制挡住。目标接口没有使用不可预测的CSRF防护参数Token或者防护可以被绕过。第三点是关键。Cookie本身只是浏览器自动携带的一句话这句话不能证明用户此刻想这么做。如果接口只靠Cookie认证就等于门禁卡插进去就能开门至于是谁的手插进去的服务器并不关心。任何能触发HTTP请求的东西——链接、图片、表单、脚本——都可能变成那把手。1.3 CSRF和XSS容易混淆很多人会把CSRF和XSS放在一起说但两者本质不同。XSS是攻击者在页面里注入脚本直接借用受害者的浏览器去执行恶意代码CSRF则是攻击者在自己控制的页面里构造请求让受害者的浏览器代替他去提交。一个是往你家客厅里塞了个遥控器另一个是从门外隔着窗户按你家的遥控器。两者可以组合比如站内有一个XSS漏洞攻击者先用XSS在目标站同源环境下读取页面的Token再利用这个Token发起CSRF这时候防御就不能只靠Token了。这个点后面讲HIGH级别的时候还会再提到。2. 搭建DVWA靶场练习CSRF之前的环境准备2.1 为什么拿DVWA当练习场DVWADamn Vulnerable Web Application是专门用来合法练习Web漏洞的靶场里面每个漏洞都对应着真实世界中出现过的薄弱写法。它把CSRF单独做成了一个模块还分成LOW、MEDIUM、HIGH、Impossible四个等级LOW对应完全没有防护的代码MEDIUM对应常见的部分防护HIGH对应引入Token的防护Impossible则展示理想的防御写法。对学习CSRF来说在一个可控环境里把每一级都走一遍比看一百篇理论文章都有效至少我能亲手验证到底怎么改包才能绕过。2.2 本地部署的两种方式我推荐Docker方式一条命令就能把环境跑起来docker run -d -p 80:80 vulnerables/web-dvwa如果本机没有装Docker也可以用PHP集成环境手动部署把DVWA源码放到Web根目录修改config目录里的配置文件填入数据库账号密码然后访问首页完成初始化。用Docker的好处是隔离干净测完随时把容器删掉重建手动部署的好处是方便改源码、查看日志适合想深入研究的同学。需要注意DVWA默认带演示数据第一次访问时要点一下页面上的Create / Reset Database它会自动创建数据库和表。如果后续测试把数据弄乱了随时可以再重置。这个功能对练习CSRF尤其有用因为CSRF实验本身会修改密码如果后面忘了改成什么重置数据库就能恢复初始状态。2.3 登录与切换难度默认登录账号基本都是admin/password。登录成功后在页面的DVWA Security里把安全级别改成LOW、MEDIUM或HIGH再进入CSRF模块就能看到对应代码。这里有个小技巧DVWA每一级的前面会直接展示当前难度的PHP源码所以即使不看服务器文件夹里的原始文件也能在页面上研究代码逻辑弄清楚这一级为什么能被利用。练习CSRF时建议每次切换难度后都重置一次数据库。因为切换难度不会自动清理数据如果上一轮测试把密码改成了别的内容这回登录状态可能已经丢失重置数据库可以恢复到初始的admin/password账号状态避免被自己的测试结果卡住。3. LOW级别实战一次毫无防备的密码修改请求3.1 先读源码找到触发点把DVWA安全级别调到LOW进入CSRF模块页面就是一个修改密码表单输入新密码和确认密码提交。重点在这里它用的是GET请求而且服务端代码基本没有校验。我截取核心逻辑给大家看不同版本细节稍有差异但思路一致if (isset($_GET[Change])) { $pass_new $_GET[password_new]; $pass_conf $_GET[password_conf]; if ($pass_new $pass_conf) { // 更新数据库中的密码 } }没有Token、没有校验来源只判断两个密码是否相等漏洞非常直观。任何人只要构造一个带password_new和password_conf参数且Change存在的请求就能修改当前登录用户的密码。真实项目中如果有类似代码那基本就是CSRF的教科书级反面案例。3.2 构造一个最原始的CSRF利用先用正常方式提交一次用Burp Suite抓包会看到类似下面的请求GET /dvwa/vulnerabilities/csrf/?password_new123456password_conf123456ChangeChange HTTP/1.1 Host: 127.0.0.1 Cookie: PHPSESSIDxxxxxxxx参数全部暴露在URL里这就等于把利用方法写在脸上了。攻击者只需要让已登录用户打开自己精心构造的URL或页面。最简单的形式是一个链接a hrefhttp://127.0.0.1/dvwa/vulnerabilities/csrf/?password_newhackedpassword_confhackedChangeChange点击查看照片/a受害者在已登录状态下点击这个链接浏览器会带着他的会话Cookie去请求这个URL密码就被改成hacked。不过更隐蔽的方式是图片标签因为图片的加载不需要用户点击img srchttp://127.0.0.1/dvwa/vulnerabilities/csrf/?password_newhackedpassword_confhackedChangeChange width0 height0 /页面加载时这个图片请求会自动发出。我把这个HTML放在一个本地测试页面里开着DVWA的登录态一刷新页面DVWA里的密码就变了。整个过程不需要任何脚本也不涉及跨域限制——因为请求只是简单的GET浏览器完全允许。这个体验很直观CSRF利用夸张起来连JavaScript都不需要。3.3 LOW级别给我们什么启示第一指定用于状态变更的接口能不用GET就不用GET。GET请求天然适合被植入到链接和图片里是最容易触发的CSRF场景。第二DOM里任何可以自动加载的资源img、script、iframe都可能成为CSRF的载体不只是表单。第三LOW级别的代码看起来简单到没人会这么写但在一些内部系统或者老项目里没有Token、又用GET处理状态变更的接口至今都不算罕见。DVWA只是把这种场景放大给你看让你知道漏洞不是只在演示代码里存在。4. MEDIUM级别绕过Referer校验是怎么被骗过去的4.1 源码里的Referer校验把安全级别切到MEDIUM再回到CSRF模块源码里多了这样一个条件if (stripos($_SERVER[HTTP_REFERER], $_SERVER[SERVER_NAME]) ! false) { // 处理密码修改 }逻辑是检查HTTP头里的Referer字段是否包含当前服务器的主机名。如果请求来自DVWA站内Referer会像http://127.0.0.1/dvwa/vulnerabilities/csrf/这样里面带着127.0.0.1校验通过如果是外部恶意页面发来的请求Referer一般是攻击者的域名不含目标主机名校验失败。这里要特别强调它用的是stripos做子串匹配不是严格比对完整域名。这意味着只要Referer字符串里出现目标主机名就算通过。这一下就打开了绕过空间。如果作者改成用parse_url取host后严格比对这个绕过就没那么轻松了。所以在分析防护代码时第一件事就是看校验逻辑是包含还是完全相等。4.2 为什么去掉Referer反而过不了很多初学者第一反应是既然Referer会被拦截那干脆让浏览器不发送Referer不就行了我在DVWA里试过用meta namereferrer contentno-referrer或者直接从Burp里删掉Referer头结果DVWA同样拒绝了请求。原因很简单stripos在HTTP_REFERER不存在时拿到的是空值或false与目标服务器名比较后条件不成立校验失败。这说明DVWA的MEDIUM防护不是有空Referer就能过而是必须包含服务器名才能过。这个细节比很多人印象里的Referer校验看Referer存不存在更严格也更接近某些真实系统的写法。理解这一点才能理解后面的绕过思路为什么成立。4.3 利用部分匹配缺陷完成绕过既然校验是包含服务器名那就让Referer里带上服务器名同时请求又确实是从攻击者控制的地址发出来的。我的实操验证过程是这样的先用Burp Suite正常抓一次DVWA的密码修改请求。在Repeater里把Referer改成http://127.0.0.1.evil.com/。发送请求DVWA接受了密码被修改。为什么能过因为127.0.0.1.evil.com这个字符串里包含127.0.0.1stripos匹配成功。在真实攻击场景中如果目标服务器名是一个普通域名比如bank.example.com攻击者可以去注册一个形如bank.example.com.evil.com的域名让浏览器发出的Referer自然带上目标服务器名从而绕过校验。只要这种包含匹配存在攻击者就有机会把合法来源拼到自己的域名里。当然用Burp改包算是一种工具性绕过现实攻击者不能直接改浏览器的请求头。但漏洞的本质是校验逻辑不够严格——攻击者不需要直接改包只需要找到能让Referer包含目标服务器名的合法场景。比如在攻击者控制的域名里加上目标服务器名字段就是一个真实可行的路径。DVWA的MEDIUM等级就是让你看清这一点。4.4 Referer校验为什么天生不可靠Referer头本身是浏览器自己加的一个请求来源说明它不是用户身份的一部分也不是安全凭证。浏览器里有很多因素会导致它缺失或变化用户隐私设置、代理、某些协议跳转、CSP的referrer策略等。服务端往往无法严格验证这个值而且很多开发者在实现时都是做个子串匹配结果就是看起来有防护实际上漏洞还在。这也是为什么Web安全社区后来普遍转向Token机制的原因。Referer和Origin头可以作为辅助信号但单独拿来防CSRF始终是尽力而为。5. HIGH级别防御Token校验是怎么挡住伪造请求的5.1 源码里的同步令牌切到HIGH级别CSRF模块的页面里多出一个隐藏字段user_token。每次打开页面时服务端都会生成一个随机的Token值同时存在Session里并放进表单。提交时服务端会把表单里的Token和Session里的Token做比对不一致就拒绝执行。页面生成部分的逻辑大致如下$_SESSION[session_token] md5(uniqid()); // 输出到表单的 hidden inputinput typehidden nameuser_token value...处理请求时先校验checkToken($_REQUEST[user_token], $_SESSION[session_token]); // 校验通过后才处理密码修改这就是同步令牌Synchronizer Token Pattern的标准流程。从防御原理上这比Referer校验强得多Token对每个会话都是独立随机生成的攻击者在他自己的页面上无法提前预知Token值。如果用户浏览器停留在攻击者的页面页面里构造的表单没有Token提交后直接被拒绝。5.2 我实际测试的结果我在HIGH级别下尝试了三种方式记录如下直接用Burp重放之前LOW的构造请求不携带user_token结果密码没有被修改。服务端在Token校验那一环就停住了。手动打开DVWA的CSRF页面把页面里的Token复制到攻击表单里第一次提交成功但再次打开DVWA的CSRF页面时Token已经变了旧的Token再次提交失败。说明Token与Session绑定而且每次页面刷新都会变化。尝试在攻击页面里用JavaScript读取DVWA页面中的Token但由于同源策略限制攻击者脚本无法跨域读取目标页面的DOM拿不到Token。这就是HIGH级别能在绝大多数情况下防住CSRF的根本原因攻击者缺少一个不可预测的请求参数。所谓防CSRF本质上是让请求里存在一个攻击者猜不到、也读不到的值而这个值又能被服务端验证。5.3 Token方案不是万能组合攻击依然存在HIGH级别防得住直接伪造但我在靶场里也验证过一个组合场景如果DVWA的另外一个模块存在反射型XSS攻击者可以先通过XSS在目标站同源环境下读取当前页面的Token再把它拼到构造的CSRF请求里。这样一来Token校验就被绕过了。这个链条本质上已经不再是单纯的CSRF问题而是多个漏洞的组合利用。真实项目里另一类风险是Token自身实现得不够严谨Token放在URL参数里导致出现在历史记录和Referer中Token用固定密钥和时间戳生成导致可预测Token不绑定Session导致可以复用JSONP接口或CORS误配置导致Token可以被跨域读取。这些都是我在代码审计时经常看到的问题。所以DVWA的HIGH等级是一个很好的起点但它不等于终点。6. 从靶场回到真实项目CSRF防御的落地做法6.1 面向开发者的防御清单在真实项目中我建议按优先级做以下几件事所有状态变更请求都校验CSRF TokenToken必须随机、绑定Session、定期失效。这是最核心的一层。状态变更接口限制为POST等方法尽量避免GET触发副作用并且不要接受通过URL参数提交的Token。给Cookie设置SameSite属性值设为Lax或Strict。主流浏览器现在会默认拦截很多跨站请求的Cookie携带能挡住相当一部分简单CSRF。对高价值操作改密码、改绑定手机、转账增加二次验证比如要求输入当前密码、短信验证码。这既是安全设计也是良好的产品体验兜底。如果必须检查Origin或Referer要严格比对完整来源而不是做包含匹配。优先校验Origin头因为现代浏览器在跨站请求时通常不会带OriginCORS场景下也会被限制。使用框架自带的CSRF防护组件不要自己拍脑袋写。6.2 安全的测试姿势练习DVWA或者做Web安全测试时我通常会遵守这些原则只在靶场或获得授权测试的环境中做CSRF利用不要拿别人的线上业务测试这是底线。测试CSRF时用Burp Suite和浏览器开发者工具双开抓包观察请求的完整头确认Token、Cookie、Referer等参数在请求里的样子。比如DVWA的MEDIUM等级光用浏览器看表面行为可能看不出Referer的作用抓包才能看清。测试完要恢复现场。DVWA里的CSRF模块会改密码我习惯测试前截个图测完重置数据库或改回密码避免影响下一次练习。6.3 沉淀下来的判断直觉经过这几级练习我现在拿到一个新接口时会本能地问三个问题这个接口会不会改变状态改密码、删数据、发消息都算。当前请求的身份凭证是什么是Cookie还是Header里的认证信息请求里有没有不可预测且服务端强制校验的参数如果没有就存在CSRF风险。这三问基本就是CSRF漏洞的判题公式。练完DVWA的CSRF模块把这个公式用在代码审计或渗透测试里会比单纯背防御方案有用得多。我在实际项目中见过不少加了Token但还是可以被绕过的情况原因大多出在Token生成逻辑太弱或者校验不严Token直接放Cookie里且没有绑定会话Token用时间戳加固定密钥拼接校验时用宽松比较导致类型混用。DVWA HIGH级别那种Token加Session的标准写法放到真实项目里已经能防住绝大多数CSRF攻击如果再补上SameSite、方法限制、二次确认这几层基本就接近Impossible级别了。CSRF这个漏洞带给我的最大提醒是登录状态本身不代表操作者意愿。浏览器会自动携带Cookie有些用户操作其实是别人的意志借了你的身份。真正安全的系统应该让每个敏感操作都能被验证为用户本人意图。这是我练完DVWA之后在做任何Web功能设计时都会反复想起的一句话。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑