CSRF攻击原理深度解析:从漏洞复现到Token防御实战

📅 2026/7/1 17:53:42
CSRF攻击原理深度解析:从漏洞复现到Token防御实战
1. 从一次“被点赞”说起理解CSRF的本质你有没有遇到过这种情况早上打开某个社交平台发现昨晚莫名其妙给一堆不认识的人点了赞或者关注了几个奇怪的账号。你确信自己没有操作过账号密码也没有泄露。这种“灵异事件”的背后很可能就是跨站请求伪造在作祟。作为一名在应用安全领域摸爬滚打了十多年的老兵我处理过太多由CSRF引发的安全事件从用户账户被恶意操作到后台管理功能被篡改甚至造成直接的经济损失。今天我就结合自己踩过的坑和修复过的案例带你彻底拆解CSRF攻击让你不仅能看懂漏洞原理更能亲手搭建环境复现、掌握防御之道。简单来说CSRF攻击就是攻击者欺骗用户的浏览器让其以用户的名义向一个用户已经认证过的网站发送非本意的请求。关键在于“伪造”和“跨站”。你的浏览器带着你的“身份凭证”比如Cookie乖乖地执行了来自另一个网站的恶意指令。这就像你家的狗只听你的口令但攻击者模仿了你的声音在门外发出指令你的狗就乖乖把门打开了。理解这一点是防御所有此类攻击的基石。无论你是刚入门的安全爱好者、正在学习渗透测试的学生还是需要为自己项目加固防线的开发者搞懂CSRF都至关重要。2. CSRF攻击的核心原理与实现条件拆解要防御攻击必须先成为攻击者理解其每一步的动机与限制。CSRF攻击的成功依赖于几个关键条件的同时满足缺一不可。2.1 攻击发生的三个必要条件第一用户必须在目标站点例如bank.com上处于登录状态浏览器中保存了有效的会话认证信息如Session Cookie。这是攻击的“燃料”。没有这个请求就是匿名的服务器不会受理。第二目标站点存在可以被预测或推断的敏感操作接口并且该接口没有足够的不可伪造令牌验证。例如修改密码的接口是POST /change-password转账接口是POST /transfer。这些接口的参数如目标账户、金额如果容易被猜测或通过其他信息泄露就为攻击提供了“坐标”。第三用户需要访问一个攻击者精心构造的恶意页面。这个页面可能通过邮件、论坛、社交网站等渠道传播。这是触发攻击的“扳机”。用户点击链接或仅仅是加载了页面攻击就可能自动发生。这三个条件环环相扣。我常跟团队说我们的防御目标就是不惜一切代价打破这个链条尤其是第二个和第三个环节。2.2 攻击的两种主要类型GET与POST根据目标接口使用的HTTP方法CSRF攻击的构造方式有所不同理解这点对后续的漏洞检测和防御方案选择很重要。GET型CSRF这是最简单直接的一种。如果敏感操作如删除文章、修改设置是通过GET请求完成的那么攻击就变得极其容易。攻击者只需要在恶意页面中嵌入一个图片标签、一个脚本标签或者一个自动加载的iframe其src属性指向目标URL即可。!-- 恶意页面中的代码 -- img srchttps://bank.com/transfer?toattackeramount10000 width0 height0 /当用户浏览器加载这个页面时会尝试去加载这个“图片”从而自动向银行网站发送一个带有用户Cookie的GET请求完成转账。由于图片加载失败通常不会引起用户注意攻击在静默中完成。注意在实际业务中绝对禁止使用GET方法进行任何具有“副作用”修改数据的操作这是最基本的安全编码规范。我审计代码时看到用GET删改数据基本可以一票否决。POST型CSRF现代Web应用更多使用POST方法提交数据但这并不能阻止CSRF。攻击者可以在恶意页面中构造一个隐藏的表单并通过JavaScript自动提交。!-- 恶意页面中的代码 -- body onloaddocument.forms[0].submit() form actionhttps://bank.com/change-password methodPOST input typehidden namenew_password valuehacked123 / /form /body页面加载完成后表单会自动提交同样会携带用户的会话Cookie。对于用户而言他们只是访问了一个普通网页完全感知不到背后发生的密码修改请求。2.3 攻击载荷的常见载体与传播方式理解了原理我们来看看攻击者如何把“扳机”送到用户手上。这往往是安全中最容易被忽视的“人的因素”。钓鱼邮件与链接这是最经典的方式。一封伪装成系统升级、奖品领取的邮件里面包含一个指向恶意页面的链接。邮件文案会诱导用户点击例如“您的账户存在异常请立即点击验证”。恶意网站广告与评论攻击者在论坛、博客的评论区插入包含恶意代码的图片或链接。当用户浏览这些页面时攻击就被触发。我曾经见过一个案例某大型论坛的个人资料页允许使用img标签结果成了CSRF攻击的重灾区。第三方应用与插件如果用户安装了不安全的浏览器插件或访问了被入侵的第三方网站这些应用可以轻易地在后台发起跨站请求。结合其他漏洞扩大影响CSRF经常与XSS跨站脚本漏洞结合。如果一个站点存在存储型XSS攻击者可以将CSRF载荷持久化地注入到网站页面中影响所有访问该页面的已登录用户危害性呈指数级放大。3. 亲手搭建靶场深度复现CSRF攻击全流程“纸上得来终觉浅绝知此事要躬行。”光讲理论不够我们必须亲手操作一遍。这里我选择两个经典的、非常适合学习的开源靶场Pikachu和DVWA。它们环境纯净漏洞点清晰是理解CSRF的绝佳工具。3.1 环境准备与靶场部署首先你需要一个基础的Web运行环境。我强烈推荐使用Docker来部署这能保证环境隔离避免弄乱你的本地系统。如果你还没有安装Docker请先到官网下载安装。部署Pikachu靶场Pikachu是一个覆盖了多种Web漏洞的综合性靶场其中CSRF部分设计得非常典型。# 拉取Pikachu的Docker镜像这里假设有官方或社区维护的镜像实际可能需要从GitHub克隆 # 如果没有现成镜像我们可以通过Git克隆源码并用PHP内置服务器运行 git clone https://github.com/zhuifengshaonianhanlu/pikachu.git cd pikachu # 使用PHP内置服务器快速启动监听8080端口 php -S 0.0.0.0:8080访问http://localhost:8080你会看到Pikachu的首页。按照提示初始化数据库通常有一个安装页面或脚本然后就可以开始使用了。部署DVWA靶场DVWADamn Vulnerable Web Application是另一个极其著名的漏洞练习平台其CSRF模块难度可调适合循序渐进地学习。# 使用Docker一键部署DVWA是最方便的方式 docker run --rm -it -p 8081:80 vulnerables/web-dvwa访问http://localhost:8081默认登录账号是admin密码是password。首次登录需要点击页面底部的Create / Reset Database按钮来初始化数据库。实操心得在本地搭建靶场时我习惯将不同的靶场运行在不同的端口上并用笔记软件记录下每个靶场的地址、账号和漏洞目录。比如Pikachu:8080/csrf,DVWA:8081/vulnerabilities/csrf/。这能极大提高后续实验的效率。3.2 复现GET型CSRF攻击以Pikachu为例登录与漏洞定位访问Pikachu首页点击左侧的“CSRF”菜单进入漏洞模块。通常里面会有“GET型”和“POST型”等子模块。理解正常流程我们先走一遍正常流程。在GET型漏洞页面你会看到一个模拟的“修改个人信息”表单可能包含昵称、邮箱、电话等字段。填写信息并提交用浏览器的开发者工具F12打开切换到Network标签页观察这个请求。你会发现它是一个GET请求所有参数都附加在URL后面像这样http://localhost:8080/vul/csrf/csrfget/csrf_get_edit.php?sex...phonenum...add...email...submitsubmit。同时请求头里会自动携带你的Cookie。构造恶意页面现在我们扮演攻击者。在你的本地创建一个新的HTML文件比如csrf_get_attack.html。核心思路是让用户浏览器自动访问上面那个长长的URL。!DOCTYPE html html headtitle有趣的猫猫图片/title/head body h1快来看这只可爱的猫/h1 !-- 利用img标签的src属性发起GET请求 -- img srchttp://localhost:8080/vul/csrf/csrfget/csrf_get_edit.php?sexattackphonenum110addhackerhomeemailattackerevil.comsubmitsubmit width0 height0 / p如果没看到猫可能是图片加载慢哦~/p /body /html注意我们把URL中的参数改成了攻击者想要设置的值如电话改成110。触发攻击确保你在Pikachu靶场是登录状态会话有效。然后在同一个浏览器中打开这个csrf_get_attack.html文件。页面看似只显示了一行文字但后台已经偷偷加载了那个“零像素”的图片从而向靶场发送了修改信息的GET请求。验证结果回到Pikachu的CSRF页面查看你的个人信息你会发现邮箱、地址等信息已经被篡改成了攻击者设定的内容。整个过程中你作为用户除了看到一个“有趣的猫猫图片”页面没有任何提交表单的动作。这个实验清晰地展示了GET型CSRF的可怕之处简单、隐蔽、无需交互。3.3 复现POST型CSRF攻击以DVWA为例POST型攻击稍微复杂一点因为需要构造一个表单并自动提交。DVWA的CSRF模块难度设为Low非常适合演示。设置DVWA难度登录DVWA将左侧安全等级设置为 “Low”。然后访问Vulnerabilities - CSRF。分析正常请求在页面上你会看到一个修改密码的表单要求输入新密码和确认密码。输入并提交用开发者工具抓包。你会发现这是一个POST请求请求体Payload里是password_newxxxpassword_confxxxChangeChange。同时请求头中有一个重要的Cookie字段和可能存在的Referer字段。构造恶意页面创建一个csrf_post_attack.html文件。这次我们需要一个隐藏的form和一段自动提交的JavaScript。!DOCTYPE html html headtitle系统安全升级通知/title/head body p您的账户需要立即进行安全升级请稍候.../p form idhackForm actionhttp://localhost:8081/vulnerabilities/csrf/ methodPOST styledisplay:none; input typehidden namepassword_new valuehacked123 / input typehidden namepassword_conf valuehacked123 / input typehidden nameChange valueChange / /form script // 页面加载后自动提交表单 document.getElementById(hackForm).submit(); /script /body /html触发与验证确保DVWA处于登录状态。在同一个浏览器中打开这个恶意HTML文件。页面会一闪而过因为表单提交后跳转了显示DVWA的修改密码结果页面提示密码已修改。此时你的DVWA管理员密码已经被改成了hacked123而你作为用户只是看到了一个“系统升级通知”的页面。踩坑记录在早期复现时我曾遇到浏览器的“同源策略”相关限制。例如某些现代浏览器对跨域表单提交有更严格的CORS限制。但在CSRF的经典场景中利用表单提交是“简单请求”且不依赖JavaScript读取响应所以同源策略通常不阻止请求的发出只阻止脚本读取响应内容而这对于CSRF攻击来说已经足够了。如果遇到问题可以检查浏览器控制台是否有CORS错误并确保靶场和恶意页面在相同的协议和端口环境下测试最好都用localhost。4. 从攻击到防御构建全方位的CSRF防护体系理解了攻击如何发生防御的思路就清晰了针对攻击的三个必要条件逐个击破。防御的核心是“确保请求来源于我信任的、有意发送的页面”。4.1 黄金标准Anti-CSRF Token同步令牌模式这是目前最主流、最有效的防御方案被OWASP强烈推荐。其原理是在用户会话中生成一个随机、不可预测的令牌Token并在每次提交敏感请求时要求请求中必须包含这个令牌服务器端进行校验。服务端实现要点令牌生成与存储当用户会话建立时生成一个高强度随机数如UUID或使用加密安全的随机函数生成将其存储在服务器的Session中同时发送给客户端。// PHP示例生成并存储Token session_start(); if (empty($_SESSION[csrf_token])) { $_SESSION[csrf_token] bin2hex(random_bytes(32)); // 生成64字符的十六进制令牌 } $token $_SESSION[csrf_token];令牌下发在需要提交表单的页面将这个令牌作为一个隐藏字段嵌入到表单中。!-- 在表单中 -- form action/change-password methodPOST input typehidden namecsrf_token value?php echo $token; ? !-- 其他表单字段 -- input typepassword namenew_password input typesubmit value修改密码 /form对于单页面应用SPA或API接口通常通过一个初始接口将Token返回给前端前端将其存储在内存或非HttpOnly的Cookie中在后续请求的Header如X-CSRF-TOKEN中携带。令牌验证服务器收到请求后比对请求中的csrf_token参数值与Session中存储的值是否一致。// 处理请求时验证 session_start(); if ($_SERVER[REQUEST_METHOD] POST) { $requestToken $_POST[csrf_token] ?? ; $sessionToken $_SESSION[csrf_token] ?? ; if (!hash_equals($sessionToken, $requestToken)) { // 令牌无效拒绝请求 http_response_code(403); die(CSRF token validation failed.); } // 令牌有效处理业务逻辑 // ... 处理修改密码 ... // 可选使用后使旧令牌失效生成新令牌防止重放攻击 $_SESSION[csrf_token] bin2hex(random_bytes(32)); }为什么Token方案有效因为攻击者构造的恶意页面无法知道这个随机Token的值。Token与用户会话绑定且每次请求可能不同如果实现了一次一用。即使攻击者诱导用户访问了恶意页面该页面也无法伪造出正确的Token从而请求会被服务器拒绝。实操心得与进阶技巧Token的绑定最好将会话ID与Token进行二次哈希绑定防止会话固定攻击导致Token被预测。双重Cookie提交对于一些API场景可以将Token设置在Cookie中但必须是前端JavaScript可读的即非HttpOnly前端脚本读取该Cookie并将其作为自定义Header如X-CSRF-TOKEN或请求参数发送。服务器同时验证Cookie中的Token和请求中的Token是否一致。这利用了“同源策略”——恶意网站无法读取目标网站的Cookie。注意Token的泄露确保Token不通过URLGET参数传输以防被浏览器历史、Referer头或日志记录泄露。避免将Token放在全局JavaScript变量中以防被站内XSS漏洞窃取。4.2 利用同源策略校验请求来源虽然不能作为唯一防线但可以作为有效的补充手段。检查Origin Header浏览器在发起跨域POST请求或复杂的CORS请求时会发送Origin头。服务器可以检查这个头是否来自预期的域名白名单。对于同源请求浏览器可能不发送Origin头但会发送Referer头。$allowedOrigins [https://www.mytrustedsite.com, https://app.mytrustedsite.com]; $origin $_SERVER[HTTP_ORIGIN] ?? ; if (!in_array($origin, $allowedOrigins)) { // 可降级检查Referer $referer $_SERVER[HTTP_REFERER] ?? ; if (parse_url($referer, PHP_URL_HOST) ! mytrustedsite.com) { die(Invalid request origin.); } }检查Referer Header检查HTTP Referer头是否来源于本站。但需要注意Referer可能被用户浏览器设置禁用或者在某些场景下如从HTTPS跳到HTTP被剥离存在一定的误杀率。因此它更适合作为辅助验证而非主要手段。4.3 关键操作增加二次确认对于特别敏感的操作如转账、修改核心密码、删除重要数据等强制要求用户进行二次交互确认。例如在提交表单前弹出模态框要求输入登录密码、短信验证码或使用U盾等。这虽然不是纯粹的技术防御但能从用户交互层面增加攻击难度属于“深度防御”理念的一部分。4.4 设置Cookie的SameSite属性这是现代浏览器提供的一个强大的内置防御机制。通过设置Cookie的SameSite属性可以控制Cookie在跨站请求时是否被发送。SameSiteStrict最严格Cookie仅在同站请求即当前网页的域名与请求目标域名一致时发送。这意味着用户从百度点击链接到你的网站首次访问时是不携带登录态Cookie的。SameSiteLax默认值在现代浏览器中。允许在顶级导航如点击链接时发送Cookie但在跨站的子请求如图片、iframe、AJAX中不发送。这能有效防御大多数CSRF攻击同时不影响用户体验。SameSiteNoneCookie在所有上下文中发送但必须同时设置Secure属性即仅限HTTPS。对于会话Cookie设置SameSiteLax或Strict是当前防御CSRF最简单有效的方法之一。在服务端设置Cookie时// PHP设置会话Cookie的SameSite属性 session_set_cookie_params([ lifetime 0, path /, domain , secure true, // 生产环境应始终为true httponly true, samesite Lax // 或 Strict ]); session_start();5. 实战中的疑难杂症与排查技巧在实际的渗透测试和代码审计中CSRF漏洞的发现和防御的验证并非总是一帆风顺。下面分享一些我积累的常见问题场景和排查思路。5.1 如何高效地检测CSRF漏洞手工测试流程步骤一定位敏感操作。使用爬虫工具如Burp Suite的爬虫或手动浏览找出所有状态变更的端点登录/注销、修改资料、增删改查、支付、授权等。步骤二分析请求。用代理工具抓包检查这些请求是否包含唯一的、不可预测的令牌如csrf_token,authenticity_token,X-CSRF-TOKENheader。如果没有则存在风险。步骤三尝试构造POC。对于疑似漏洞点按照我们前面复现的方式尝试构造一个简单的HTML页面模拟攻击。如果操作成功则漏洞确认。步骤四检查依赖防御。如果使用了Origin/Referer检查尝试在请求中移除、修改或伪造这些Header看是否能够绕过。工具辅助Burp Suite的CSRF PoC Generator功能非常强大。在抓到一个请求后右键选择Engagement tools - Generate CSRF PoCBurp会自动生成一个用于测试的HTML页面你可以微调后直接在浏览器中测试。5.2 常见防御机制的绕过思路仅用于安全测试了解攻击者视角才能更好地防御。以下是一些历史上出现过的、针对不完整防御措施的绕过技巧用于在授权测试中评估防御的强度。Token防御被绕过Token未绑定会话如果Token是全局的或者与用户无关攻击者可以先登录自己的账户获取一个有效Token然后用在针对其他用户的CSRF载荷中。Token可预测如果Token基于时间戳或用户ID等弱熵源生成攻击者可能预测出其他用户的Token。Token在GET请求中Token通过URL参数传递可能通过Referer泄露给其他网站。站内XSS窃取Token如果网站存在XSS漏洞攻击者可以先用XSS窃取用户的Token然后再发起CSRF攻击形成组合拳。Referer检查被绕过缺失或为空如果服务器在Referer头缺失或为空时默认放行攻击者可以构造一个不发送Referer头的请求例如从data:URL 或javascript:URL发起的请求或者使用meta标签刷新。检查逻辑缺陷如果检查逻辑是“是否包含某域名”攻击者可以注册一个子域名如www.mytrustedsite.com.attacker.com或者利用URL参数?redirect_toattacker.com等方式绕过。HTTPS到HTTP当请求从HTTPS页面发往HTTP站点时浏览器出于安全考虑不会发送Referer头可能导致合法请求被拒或非法请求被放行取决于服务器策略。重要提示这些绕过技巧仅用于在您拥有明确授权的安全测试中评估自身应用的安全性。切勿用于未授权的测试。5.3 在复杂架构中的应用API与单页面应用SPA现代前后端分离架构中CSRF防御需要一些调整。API接口对于纯API后端如RESTful API会话可能通过Token如JWT而非Cookie维护。此时CSRF风险的本质发生了变化。如果认证Token是放在Authorization Header中浏览器默认不会在跨站请求中自动添加该Header因此CSRF风险较低。但如果应用将JWT存储在Cookie中以便实现自动登录那么CSRF风险依然存在防御方案依然是使用Anti-CSRF Token或设置Cookie的SameSite属性。单页面应用SPAToken管理在用户登录后后端通过API返回一个CSRF Token前端将其存储在内存如Vuex/Redux或非HttpOnly的Cookie中。请求携带对于所有非幂等的请求POST, PUT, DELETE, PATCH前端需要主动将这个Token添加到请求Header中例如X-CSRF-Token。框架集成像Angular、Axios等库内置了CSRF Token支持通常默认从Cookie中读取名为XSRF-TOKEN的值并在每次请求时自动添加到X-XSRF-TOKENHeader中。你需要确保后端与之配合。5.4 防御方案选型与部署 checklist根据你的应用类型可以参考以下清单来选择和部署CSRF防御措施[ ]所有Web应用基础必做为会话Cookie设置SameSiteLax或Strict属性。确保所有具有副作用的操作修改数据都使用POST、PUT、DELETE等HTTP方法严禁使用GET。[ ]传统服务端渲染应用为每一个用户会话生成一个唯一的、高熵的CSRF Token。在所有状态变更的表单中包含该Token作为隐藏字段。服务器端对每一个相关请求进行Token验证。考虑对重要操作如转账实施二次验证。[ ]前后端分离/SPA/API服务如果使用Cookie进行会话管理必须实施CSRF Token防御。采用“双重Cookie提交”模式设置一个前端可读的Cookie如csrf_token前端JS读取其值并在请求时通过自定义Header如X-CSRF-Token发送。或者将认证信息完全放在Authorization Header如Bearer Token中并避免将其存储在Cookie里。[ ]补充与增强在服务器端对敏感操作的请求增加Origin或Referer头检查作为辅助验证。定期进行安全审计和渗透测试验证CSRF防御的有效性。CSRF是一个经典的、原理并不复杂但危害巨大的漏洞。它的防御手段经过多年发展已经非常成熟。核心在于不要信任任何来自浏览器的自动提交的请求必须有一个只有合法页面才知道的“暗号”。通过结合Token、SameSite Cookie和来源检查构建纵深防御体系就能将这个古老漏洞的风险降到最低。在每次代码评审时把CSRF防御作为一个必查项养成安全编码的习惯远比事后补救要有效得多。