ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CSRF跨站请求伪造原理、绕过与靶场实战:DVWA与Pikachu通关指南

CSRF跨站请求伪造原理、绕过与靶场实战:DVWA与Pikachu通关指南 很多搞Web开发的朋友第一次看到“安全提示:疑似跨站请求伪造访问已被禁止。错误编号【ese0102001】”这种页面时大概率是懵的。明明自己在后台点了个按钮或者在前端页面提交了一个表单结果直接被拦截业务都断了。这个报错说白了就是WAF或业务网关在做CSRF防护时给出的拦截响应翻译成人话就是服务器认为这个请求是伪造的不是用户真实意愿发出来的。CSRF跨站请求伪造属于那种“看起来不起眼、实际破坏力极强”的漏洞尤其在管理员后台、支付转账、修改密码这类场景里一个CSRF就能把整个账号拱手让人。今天这篇是第一期我把CSRF的原理、真实场景、DVWA和Pikachu靶场里的逐级复现以及常见的“csrf绕过”说法一次性讲透。不管是刚入门的Web安全学习者、做代码审计的开发者还是被线上误拦截问题搞到头秃的运维都能在文章里找到你要的东西。1. CSRF到底是怎么发生的1.1 三个前提条件缺一个都打不穿CSRF全称Cross-Site Request Forgery跨站请求伪造。理解它之前先要理解一个Web世界里最基础的机制HTTP是无状态的但浏览器为了让你“记住登录状态”会在收到服务器响应时种下Cookie之后你每次向同一个域名发请求浏览器都会自动把Cookie带上。这个过程对用户完全透明也正是这个“透明”的特性给了CSRF钻空子的机会。我习惯用一个餐厅服务员的类比来解释网站是餐厅你的登录状态是VIP会员卡浏览器就是服务员。你只要在餐厅消费过一次服务员之后端菜过来时不需要再验卡因为你已经在店里了。CSRF攻击者做的事就是伪造一个“端菜”的指令比如端一道你根本没点的菜而服务员看到指令觉得来源没问题就直接照做了。这个“指令”就是你浏览器里正在登录的网站请求那个“服务员”就是服务器端盲信了请求的身份凭证。CSRF能成立必须同时满足三个前提受害者已经登录目标网站并且浏览器里保留了有效的身份凭证Cookie、Session等受害者访问了攻击者精心构造的恶意页面目标网站的关键操作请求可以被我方提前预测比如URL、参数名、参数值都是固定的。这三个条件听起来简单但现实中满足的情况极其普遍。尤其很多老旧后台系统管理员常年不退出登录浏览器一直挂着有效Session这时候只要管理员手滑点了一下攻击页面里的链接攻击就完成了。1.2 GET型CSRF与POST型CSRF同样危险玩法不同CSRF按请求方式主要分两种GET型和POST型。GET型的典型玩法是攻击者把恶意请求隐藏在图片标签里受害者浏览器在加载页面时会自动发起这类GET请求。举个例子一个没有防护的修改密码接口长这样GET /user/change_password?new_passwordhacked HTTP/1.1 Host: bank.example.com Cookie: sessionvalid_session_value攻击者只需要构造这么一个HTML页面img srchttp://bank.example.com/user/change_password?new_passwordhacked styledisplay:none;受害者只要打开这个页面浏览器就会自动请求这个图片地址。因为受害者已经登录过bank.example.com浏览器自动带上Cookie服务器收到请求后以为是你本人修改了密码实际是攻击者把密码改成了自己知道的“hacked”。整个过程受害者完全无感知页面可能就是一个广告页图片还是隐藏的。POST型CSRF则是利用表单自动提交攻击页面里写一个隐藏表单配合一段脚本直接触发submit事件。这种形式在直觉上让人觉得“POST更安全”因为在很多开发者的认知里POST请求比GET请求更难被第三方伪造。但事实上POST请求照样可以被跨站提交只是需要多写几行HTML而已。form actionhttp://bank.example.com/user/change_password methodPOST input typehidden namenew_password valuehacked /form script document.forms[0].submit(); /script这段代码在页面加载时会自动提交表单浏览器会带上目标站点的Cookie服务器一样会认为是本人操作。所以千万别觉得“我把敏感操作改成POST就安全了”后端不做校验换什么请求方式都是白搭。1.3 CSRF与XSS的区别别再把这两个混为一谈很多人刚接触Web安全时会混淆CSRF和XSS这两个漏洞虽然经常被一起提但攻击逻辑和防御思路完全不同。我把对比写成了表格方便对照记忆。对比维度CSRF跨站请求伪造XSS跨站脚本攻击核心目标伪造受害者的请求在受害者浏览器中执行恶意脚本利用的信任关系服务器信任浏览器携带的Cookie浏览器信任服务器返回的页面内容是否需要执行JS不需要纯请求伪造即可需要核心就是让脚本跑起来攻击结果替受害者执行任意操作窃取Cookie、篡改页面、伪造操作等受害者交互访问恶意页面即可访问恶意页面或带恶意内容的正常页面防御重点Token、SameSite、Origin校验输入过滤、输出编码、CSP简单说XSS是攻击者“在受害者眼皮底下干坏事”CSRF是攻击者“借受害者的手去干坏事”。XSS需要把代码注入页面并被执行CSRF只需要制造一个合法请求XSS能偷走你的登录凭证CSRF没用凭证也能调用你浏览器里的凭证。搞清楚这个区别后面看漏洞识别逻辑时会轻松很多。2. 真实世界里的CSRF攻击场景与绕过思路2.1 高危场景盘点哪些功能最容易被盯上CSRF的破坏力更像“指哪打哪”攻击者需要提前知道目标站点有哪些可被利用的接口。我在做代码审计时一般会优先排查下面这几类高危功能这些也是漏洞平台和渗透测试里最常见的CSRF考点修改密码、重置密码、绑定或解绑手机号/邮箱转账、提现、修改收款账户修改收货地址、订单状态投票、点赞、关注等互动类操作后台管理里的修改配置、删除数据、添加管理员第三方OAuth登录的绑定与解绑。这些操作的共性是什么呢一是影响大二是请求可预测。以修改密码为例如果接口只接收new_password一个参数没有任何Token或二次校验攻击者构造的恶意链接一旦被点击用户密码立刻被改掉接下来就是账号被接管。绑定手机号更狠攻击者提前把自己的手机号绑到受害者账号上以后所有短信验证码都会发到攻击者手里。有人可能会问攻击者怎么知道受害者在哪个网站有账号、登录了没有现实攻击中靠的是组合场景。攻击者可能在普通网站B里植入CSRF攻击代码然后引导受害者访问。受害者刚好登录着目标网站A攻击就成功了。这种“跨站”的性质决定了CSRF非常难被受害者察觉也很难通过日志追溯。2.2 所谓的“CSRF绕过”本质是防御机制存在缺陷网上经常会看到“csrf绕过”这个词听起来很玄乎其实它不是因为CSRF漏洞本身“绕不过”而是因为开发者为了防御CSRF做了一些措施但这些措施可以被攻击者钻空子。绕过的思路本质上是围绕防御机制展开的最常见的几类如下。第一类是绕过Referer校验。很多开发者知道要校验请求来源于是在后端判断了Referer头。常见写法是判断Referer里是否包含当前域名比如if (strpos($_SERVER[HTTP_REFERER], example.com) false) { die(非法来源); }这种判断在大部分情况下是有效的但存在两个漏洞一是Referer可以被攻击页面控制比如攻击者把恶意页面放在一个URL里包含目标域名的路径上二是有很多浏览器在特定场景下会发送空Referer比如从data:协议或某些特殊跳转方式进入页面时。如果服务器只判断“Referer非空且包含域名”攻击者构造一个不带Referer的请求就能通过。DVWA靶场的Medium级别就是这个思路后面我会详细演示。第二类是绕过Token校验。Token机制被普遍认为是最可靠的CSRF防御手段但如果服务器端实现不严谨照样能绕过。比较典型的两类问题一个是用GET参数传递TokenToken出现在URL里通过Referer、日志、浏览器历史记录泄露另一个是Token校验逻辑写反了比如只在用户提交时校验但完全没有把Token和服务端Session绑定攻击者随便填一个字符串可能也能通过。有些系统会把Token放在Cookie里服务端从Cookie读取并校验这种实现如果同时开启了CORS跨域允许也可能被绕过。第三类是利用同站点子域或JSONP接口。如果一个主站的所有子域都共享父域Cookie而某个子域存在XSS或CORS配置错误攻击者就能从那个子域“合法地”发起请求这种场景已经不是单纯的CSRF而是多个漏洞的组合利用。我在实际排查中见过不少系统把防御做成“半吊子”校验了Referer但允许空Referer生成了Token但没绑定Session或者给接口加了Token但前端页面里直接写死了一个固定值。这些都是典型的“表面防护”对抗自动化工具都费劲更别说人了。2.3 从漏洞靶场看CSRF的利用链为什么后台系统是重灾区很多漏洞靶场平台包括一些在线的挑战环境比如dig2pen这类把漏洞拆成关卡的平台都会把CSRF设置成独立考点考查的核心点只有一个你能不能识别出某个请求里没有任何不可预测的参数。我之前在测一个内部管理系统时印象很深。系统登录后有修改个人资料的功能包括姓名、手机号、邮箱。一开始我按常规思路测XSS结果输入过滤做得挺严没什么收获。后来抓包看了一下修改资料的请求发现整个请求里只有一个当前用户ID和几个表单字段既没有Token也没有当前密码校验也没有校验Referer。这就等于告诉所有人你随便构造一个表单让管理员点了他的手机号邮箱就被改了后续找回密码、跳转绑定流程全都可能被串起来利用。这就是CSRF在后台系统中的真实威胁。后台管理员权限高可操作的数据多而且管理员通常不会频繁退出登录。攻击者只要让管理员在登录状态下访问一个外部页面后台的数据就可能是别人的了。所以为什么很多企业安全规范里要求后台系统必须开启CSRF防护原因就在这里。防护的意义不在于攻击者“能不能伪造请求”而在于你是否提供了一种让服务器验证“这是用户真实意图”的机制。3. 靶场实战复现DVWA与Pikachu逐级通关3.1 DVWA Low级别无防护的裸奔状态DVWA是Web安全学习者最常用的靶场里面专门有一个CSRF模块分了Low、Medium、High三个级别。我强烈建议新手把这个模块从Low到High完整走一遍因为这三关恰好对应了CSRF防御从无到有、从弱到强的进化过程。先看Low级别。登录DVWA后进入CSRF模块页面是一个修改密码的表单只要求输入两次新密码然后提交。我抓了一下请求长这样GET /dvwa/vulnerabilities/csrf/?password_new123456password_confirmed123456ChangeChange HTTP/1.1 Host: 127.0.0.1 Cookie: PHPSESSIDxxxxxx注意这个请求的关键特征第一是GET请求第二没有任何Token参数第三没有校验Referer第四修改密码不需要提供当前密码。四个特征叠加在一起等于明文裸奔。后端代码更是直接大概就是校验两次密码一致后直接更新数据库没有任何CSRF防护逻辑。复现攻击非常简单我在攻击机另一台虚拟机的Apache目录下放了一个HTML文件内容就是之前写过的那种图片标签img srchttp://127.0.0.1/dvwa/vulnerabilities/csrf/?password_newhackedpassword_confirmedhackedChangeChange然后在一台已经登录DVWA的浏览器里访问这个恶意页面。页面打开后我用新密码“hacked”尝试登录直接成功。Cookie被浏览器自动带过去服务器根本不会区分这个请求是从哪个页面发出来的。这个级别的复现可能让人觉得“这也太简单了”但真实世界里有大量老系统就是这个状态。代码审计的时候如果看到关键操作是GET上传参、没有Token不需要继续往下看直接可以标记为高风险。3.2 DVWA Medium级别Referer校验与绕过Medium级别的代码多了两行关键校验逻辑是从$_SERVER[HTTP_REFERER]中检查是否包含目标服务器的主机名。在DVWA默认配置下服务器名是127.0.0.1所以后端判断的是Referer头里是否包含127.0.0.1。这里我先试验最简单的直接攻击方式把Low级别那个恶意HTML原样放在攻击机Apache目录里浏览器访问后抓包发现请求被拒绝了。原因是我的攻击页面地址是http://攻击机IP/csrf_attack.html浏览器发出的请求头里带的是Referer: http://攻击机IP/csrf_attack.html显然不包含127.0.0.1所以DVWA判定为非法来源并拒绝执行。那怎么绕过呢我上面提到过很多服务器的Referer校验只检查“非空且包含域名”也就是说如果我能让Referer头里包含目标域名字符串就能骗过它。具体做法有几种第一种最简单把攻击页面文件名改成包含目标域名的形式比如http://攻击机IP/127.0.0.1.html这样Referer的值里就出现了“127.0.0.1”字符串。我实测这个思路有效DVWA Medium就被这样绕过了。第二种思路是让浏览器完全不发送Referer。怎么触发空Referer呢在恶意页面里使用data:协议或者某些特殊跳转方式打开目标地址浏览器可能不发Referer。但是这里有一个前提服务器校验逻辑必须允许Referer为空。很多系统的判断是if (isset($_SERVER[HTTP_REFERER])) { // 检查是否包含域名 } else { // 直接放行 }这种“Referer为空就放行”的写法很常见因为开发者想兼容直接输网址访问、邮件客户端打开链接等场景结果反而留了个后门。DVWA Medium的代码处理得相对严格一点但我用第二种思路测试时发现如果直接把请求发给DVWA在某些版本中也是可以绕过的。具体情况建议你自己搭个环境试一下不同语言、不同服务器的Referer处理逻辑有细微差异。3.3 DVWA High级别Token校验的实战对抗到了High级别代码加入了Token校验。源码逻辑大致是用户访问页面时生成一个随机Token并存在Session里表单提交时把这个Token作为隐藏字段一起提交服务端比对Token值和Session里存储的值不一致就拒绝请求。这意味着单纯的图片标签攻击已经失效了因为攻击者无法提前获知当前受害者的Token。我用Low和Medium的恶意页面测试全部被拦截服务端没有修改密码。那High级别是不是就绝对安全了也不是。DVWA High的实战思路是攻击页面上先通过iframe加载目标的修改密码页面从中提取Token再自动提交恶意请求。核心逻辑类似下面这段iframe idcsrf_frame srchttp://127.0.0.1/dvwa/vulnerabilities/csrf/ onloadstealToken() styledisplay:none;/iframe当iframe加载完成之后页面里已经包含了DVWA生成的Token攻击脚本可以解析iframe页面DOM取出Token值拼到构造好的URL后面再请求修改密码接口。整个过程模拟出来大概是先请你“看”一下目标站点页面然后请求一个带Token的修改密码链接。这个手法能不能成功取决于目标站点的Token是不是放在页面里以及是否允许被iframe嵌套加载。DVWA默认是允许自身被iframe加载的所以这套链路是通的。我在实际操作中为了简化还写过Burp Suite的Macro宏来自动提取Token并替换到攻击请求里在自动化检测时很好用原理和上面一样只是把iframe换成了Burp自己发的请求。另外需要提醒一点High级别的防御还存在一个隐患Token只校验随机性但没有做“一次性”处理。同一个Token在有效期内可以反复提交攻击者只要在受害者页面过期前拿到Token攻击窗口就存在。实际请求头的构造也能看到对User-Agent的校验我在实测中发现有些版本需要同步UA才能稳定通过这个细节在真实渗透时很关键。3.4 Pikachu靶场CSRF模块GET型与POST型的对比复现Pikachu是另一个我非常推荐的中文靶场它的CSRF模块设计得很直观把GET型和POST型拆成了两个不同关卡。GET型那个关卡是一个修改个人信息的表单请求长这样GET /pikachu/vul/csrf/csrfget.php?sexboyphonenum123addhackeremailhackertest.comsubmitsubmit所有参数都在URL里且没有Token校验。我构造了一个普通链接发给登录状态的浏览器点开后页面显示“修改成功”回查数据库发现信息被改成了攻击值。这个和DVWA Low的思路一模一样区别只是参数更多。POST型那个关卡就值得多写几句了。后端把请求方式改成了POST很多人会有“POST就安全了”的错觉但实际上借用自动提交表单的页面照样可以跨站打。我构造了如下恶意HTMLform actionhttp://192.168.1.101/pikachu/vul/csrf/csrfpost.php methodPOST input typehidden namesex valueboy input typehidden namephonenum value666666 input typehidden nameadd valueattacker_addr input typehidden nameemail valueattackertest.com input typehidden namesubmit valuesubmit /form script document.forms[0].submit(); /script打开页面后表单自动提交Pikachu后端没有任何校验直接更新了信息。所以从这里应该能得出一个结论POST和GET的区别对于CSRF来说根本不本质本质是服务器有没有验证“请求是否由用户真实界面发出”。Pikachu设计的魅力在于它把两种方式放在相邻的两个关卡里你只要自己动手打一遍就能直观体会到“换POST不防护换汤不换药”的道理。4. 从工程师视角看CSRF防御别再造半吊子防护4.1 Token机制落地的关键细节CSRF Token是目前最主流的防御手段但落地时有很多细节容易踩坑。一个合格的Token机制至少要满足三个条件随机性足够强不可预测、由服务端生成并保存在Session里不可伪造、提交时与Session进行比对不可绕过。我在不少代码里见过这种错误在页面里生成一个Token存在了Cookie里JS从Cookie读取再塞到表单隐藏域里一起提交。表面上看请求带了Token但攻击者从自己浏览器的Cookie里就能读出这个值并拼到恶意请求里等于没防御。正确的做法是Token放在服务端Session里页面渲染时输出到隐藏域提交时读取不依赖Cookie。还有一类问题是Token校验范围太大或太小。太大是指整个站点共用一个Token登录态切换或者报文重放时都可能被利用太小是指同一个用户在不同页面上生成不同Token但很多页面里的Token可以被另一个页面覆盖导致用户同时打开多个标签页时旧Token失效出现“提交失败”的体验问题。这个在大型前端项目里特别常见。4.2 SameSite Cookie用浏览器的机制挡掉默认跨站请求SameSite是Cookie的一个属性用来控制Cookie在跨站请求时是否携带。属性值有三个Strict、Lax、None。其中Lax是一个很实用的默认值它允许用户在地址栏输入网址访问、点击普通链接跳转时携带Cookie但阻止跨站子资源请求和跨站POST表单携带Cookie。这意味着攻击者用图片标签去触发GET请求时Cookie不会被带过去CSRF自然就断了一条腿。但要说清楚一点SameSite并不能解决所有CSRF问题。如果攻击者构造的是顶级导航跳转比如链接直接跳转到修改密码的URL那么即使开启Lax普通GET导航也可能携带Cookie。另外大量老系统在设置Cookie时没有带SameSite属性Chrome从80版本开始默认把没有SameSite声明的Cookie视为Lax其他浏览器行为不完全一致所以不能完全依赖浏览器默认值。4.3 Origin校验与Referer校验的区别刚才在Medium级别里展示了Referer校验可能被绕过所以更推荐用Origin头来校验。Origin头只在发生跨站请求时携带并且没有历史遗留问题很多浏览器在自动提交表单时也会携带Origin。简单说Origin是“这个请求是哪里发起的”Referer是“上一个页面是哪”后者涉及隐私更容易被裁减或者置空。如果一定要用Referer校验我建议这样写逻辑先从来源头的host部分做白名单匹配同时处理“Referer缺失”的场景。要么直接拒绝所有Referer为空的请求要么在业务允许该场景时用其他方式补充校验绝不能“空Referer直接放行”。4.4 网关层防护与“ese0102001”这类报错背后的逻辑回到开头那个报错。实际业务中很多系统会直接在Web网关、云WAF上配置CSRF防护策略对可疑请求统一拦截编号就是因为厂商要方便客服定位问题。我在生产环境里维护过类似的防护规则底层逻辑一般是这么判断的对登录态用户的关键操作请求校验CSRF Token如果业务已改造对新写入类请求校验Origin和Referer非白名单直接拦高频重复提交且特征明显异常例如同一个Token被来自大量不同会话的请求使用直接拦。这类网关拦截对安全是有帮助的但误伤率也不低。我自己就遇到过前端单页应用因为动态加载了第三方字体、图片导致跨域请求被误拦的案例排查半天才发现是Origin头没有按预期传过来。遇到这种报错时不要立刻怀疑是攻击先抓包看两个东西一个是请求头里有没有保留Cookie与Token另一个是Origin/Referer是不是符合网关白名单规则。5. 常见问题与排查技巧实录5.1 误拦截速查表结合我处理过的线上问题和靶场实战经验整理了一份快速排查表遇到相关报错可以直接对照。现象可能原因排查方向登录用户在正常页面提交表单被拦截网关/框架开启CSRF校验但未对单页应用做白名单检查请求的Origin头是否与页面域名一致查看网关规则Token校验一直失败刷新页面后恢复多标签页导致Token被覆盖或提交了过期Token改成每会话Token或允许一定窗口期修改密码等请求正常却收到疑似跨站请求伪造提示请求中缺少CSRF Token字段抓包确认页面渲染时是否生成Token检查表单是否包含隐藏域某些浏览器正常某些浏览器被拦SameSite属性兼容性不同统一设置SameSiteLax并在服务端做Origin兜底后台报表访问正常但第三方回调被拦回调请求不带用户登录态或不符合来源校验对回调接口配置独立的签名校验不走CSRF口令逻辑5.2 代码审计时如何快速定位CSRF风险点如果要在已有代码库里做一轮CSRF风险排查我一般按三步走。第一步是把所有改数据状态的接口列出来重点关注POST接口但千万别漏了GET链接式的操作第二步是逐个检查这些接口有没有做身份之外的“意图校验”包括Token、当前密码、一次性验证码、Origin校验第三步是针对框架自动带有的CSRF防护确认有没有在关键接口上被显式排除。我发现很多项目里的风险点并不是“完全没有防护”而是“框架开了防护但被局部关闭了”。比如某些文件上传接口、第三方回调接口为了省事加了csrf_exempt之类的注解或中间件配置直接绕过了全局CSRF防护。这种接口如果又恰好支持登录态操作风险等级就非常高。审计时可以搜一下这些豁免点逐个确认是否有签名、Token等替代校验。5.3 我在实战中的几点体会多打几遍靶场后我对CSRF最大的感受是它不像SQL注入那样需要花式构造Payload更像是一个“信任边界”问题服务器太信任浏览器带过来的身份凭证了。判断一个系统会不会中招就看它在关键操作上有没有做“用户意图确认”。如果只校验“你是不是登录用户”而不校验“你是否真的想干这件事”那CSRF就有戏。另一个体会是修CSRF时别搞成“一刀切”。有的团队为了求稳给所有接口都加了严格Token校验结果把App接口、第三方回调、分享链接全部搞挂了最后又紧急放开留下更大的口子。正确的做法是分类处理浏览器页面操作用TokenApp等非浏览器客户端用签名或自定义Header第三方回调用令牌或签名验签。我后来在项目里就是用这套组合方案既堵住了CSRF也没伤害正常业务。最后分享一个实用小技巧测试一个请求是不是存在CSRF风险最快的办法不是写脚本而是用Burp Suite打开目标请求右键生成CSRF PoC HTML。生成后在另一台电脑或另一个浏览器环境打开如果请求能执行成功那这个接口基本就是裸奔状态。这个技巧我用了很多年不管是在靶场还是在授权测试场景下都是效率和准确率兼顾的一招。
返回列表