CSRF攻击全流程解析:从原理到防御的实战指南

📅 2026/6/21 19:54:54
CSRF攻击全流程解析:从原理到防御的实战指南
1. 项目概述从一次“被点赞”说起那天下午我正喝着咖啡手机突然弹出一条通知“您刚刚在XX社区点赞了一篇名为‘如何一夜暴富’的帖子。”我愣了一下我明明在写代码根本没打开那个社区App。点进去一看账号确实给那篇帖子点了赞操作时间就在一分钟前。我的第一反应是账号被盗了赶紧检查登录记录却发现只有我自己的设备没有异地登录。再仔细回想一分钟前我在做什么哦我在另一个技术论坛浏览那个页面里好像嵌了一个看不见的图片标签。问题就出在这里——我遭遇了一次典型的CSRF攻击。这个看似简单的“被点赞”事件背后隐藏的正是今天要深入拆解的“CSRF攻击流程”。CSRF跨站请求伪造它不像SQL注入或XSS那样直接窃取数据而是像一个“提线木偶大师”在你不知情的情况下借用你的身份和权限向服务器发送它希望你发出的请求。你可能会问我的账号密码又没丢它怎么能用我的身份这就是CSRF的精妙与危险之处它利用的是浏览器对用户身份的“自动携带”机制。当你登录某个网站后浏览器通常会以Cookie等形式保存你的登录凭证。此后在你访问该网站的任何页面甚至其他网站触发对该网站的请求时浏览器都会“乖巧”地自动附上这些凭证。攻击者要做的就是构造一个请求然后想方设法让你在已登录目标网站的状态下去触发它。理解CSRF攻击流程对于任何涉及用户状态管理的Web开发者、安全测试人员乃至普通用户都至关重要。对开发者而言这是构建安全应用的必修课一个疏忽就可能导致用户数据被篡改、资金被转移。对安全人员这是渗透测试中必须检查的一环。即便你是普通用户了解其原理也能让你更警惕网络上那些“奇怪的链接”和“好看的图片”。接下来我将以一个防御者的视角带你完整走一遍攻击者实施CSRF的每一步只有透彻理解攻击是如何发生的我们才能筑起有效的防线。2. CSRF攻击的核心原理与条件拆解要成功实施一次CSRF攻击必须同时满足几个关键条件缺一不可。我们可以把它想象成一次“完美的欺诈”攻击者需要凑齐所有拼图。2.1 核心原理会话凭证的自动提交这是CSRF的基石。现代Web应用普遍使用基于Cookie的会话管理。用户登录后服务器会颁发一个会话IDSession ID存储在用户的浏览器Cookie中。这个Cookie通常被标记为HttpOnly防止JavaScript读取和Secure仅通过HTTPS传输但这并不影响CSRF。因为CSRF不关心Cookie的内容它只关心浏览器是否会自动发送它。关键点在于浏览器在向某个域Origin发起请求时会自动携带该域下的所有Cookie满足路径、过期时间等条件无论这个请求是从哪里发起的。这意味着如果你登录了bank.com那么无论你是在bank.com的页面上点击按钮还是在恶意网站evil.com的页面上点击了一个指向bank.com的链接浏览器向bank.com发出的请求都会自动带上你的登录Cookie。注意这里常有一个误解认为同源策略Same-Origin Policy会阻止这种跨站请求。实际上同源策略限制的是目标站点返回的响应数据能否被发起请求的页面读取。它并不阻止请求的发出浏览器会说“好的evil.com你要我向bank.com发个请求没问题我会照做并且把bank.com的Cookie也带上。但是bank.com返回的数据你别想看到。” 攻击者往往并不需要看到响应他们只需要请求被执行。2.2 攻击成功的三个必要条件关键操作未受保护目标网站存在通过GET、POST等简单请求就能执行的重要操作如修改密码、转账、发表评论、点赞等。这些操作仅依赖会话Cookie进行身份验证没有其他校验机制。用户已登录并保持会话受害者必须已经登录了目标网站并且会话尚未过期。这样浏览器中才存有有效的身份凭证。用户被诱骗触发恶意请求攻击者需要诱导用户去访问一个精心构造的页面恶意网站、论坛帖子、邮件内容等该页面包含了向目标网站发起操作的代码。2.3 攻击流程的宏观视角整个攻击流程可以概括为以下几步侦察攻击者寻找目标网站的可利用端点。例如通过观察表单提交、分析API接口找到一个用于修改邮箱的URLPOST https://target.com/profile/change_email 参数为new_emailattackerevil.com。构造攻击者根据找到的端点编写一个能自动或诱导用户提交该请求的HTML页面。如果目标是GET请求可能是一个img src“...”标签如果是POST请求则是一个自动提交的form。投递攻击者通过多种渠道传播这个恶意页面链接如钓鱼邮件、论坛灌水、社交网站评论甚至购买网站广告位。触发受害者点击链接或加载了包含恶意代码的页面。此时如果受害者已登录目标网站浏览器就会自动携带Cookie向目标网站发出恶意请求。执行目标网站服务器收到请求验证Cookie有效便认为是用户本人的合法操作于是执行修改邮箱或转账等指令。攻击完成。这个过程用户可能毫无察觉页面可能一闪而过或者只显示一张“加载失败”的图片但危害已经造成。3. 攻击载荷的构造手法深度解析理解了原理我们来看看攻击者具体是如何“制作武器”的。根据目标操作使用的HTTP方法不同构造恶意载荷的方式也各异。3.1 针对GET请求的攻击构造这是最简单、最常见的一种。很多Web应用早期设计不佳会用GET请求来执行修改操作例如GET https://bank.com/transfer?toattackeramount1000攻击者构造起来不费吹灰之力。他只需要在恶意网页中嵌入一个“不可见”的元素其src或href属性指向这个URL即可。浏览器在加载页面时会自动尝试加载这些资源从而发出请求。经典示例1图片标签攻击!-- 受害者看到的是一个“图片加载失败”的图标甚至什么都没有 -- img src“http://bank.com/transfer?toattackeramount1000” width“0” height“0” /当用户访问包含这段代码的页面时浏览器会尝试加载这张“图片”随即向银行网站发出转账请求。因为用户已登录Cookie被自动带上服务器便执行转账。经典示例2链接标签攻击!-- 诱惑用户点击 -- a href“http://bank.com/transfer?toattackeramount1000” 点击领取你的百万大奖 /a或者通过script标签加载一个JS文件其URL同样可以附带参数。实操心得在实际安全测试中我常用Burp Suite的“Engagement tools” - “Generate CSRF PoC”功能来快速生成这类测试载荷。它会自动抓取你拦截到的请求并将其转换为一个完整的HTML测试页面。这对于验证GET型CSRF漏洞非常高效。但切记这只能用于你拥有测试权限的系统切勿用于未授权的攻击。3.2 针对POST请求的攻击构造现代应用更规范重要操作通常使用POST请求。POST请求的参数在请求体中不能简单地用src属性触发。攻击者需要构造一个表单并利用JavaScript自动提交。示例自动提交表单攻击body onload“document.forms[0].submit()” !-- 页面一加载就自动提交表单 -- form action“http://bank.com/transfer” method“POST” input type“hidden” name“to” value“attacker” / input type“hidden” name“amount” value“1000” / input type“hidden” name“csrf_token” value“” / !-- 如果目标站点有CSRF Token但未校验这里可以留空或填假值 -- /form /body这个页面被访问时onload事件会触发表单被自动提交一个携带隐藏参数的POST请求就发往了银行网站。用户看到的可能只是一个空白页面或瞬间跳转。更隐蔽的手法AJAX请求对于使用JSON API的现代单页应用SPA攻击者可能尝试用JavaScript发起跨域AJAX请求。虽然浏览器同源策略默认会阻止读取响应但请求本身依然可能被发出尤其是使用fetchAPI且模式设置为no-cors时。不过这通常需要目标服务器的CORS策略配置极其宽松。更常见的是如果应用同时支持传统的表单提交攻击者还是会优先使用表单方式。3.3 其他变体与高级技巧JSON CSRF有些API接收JSON格式的POST数据。攻击者可以通过构造一个form将其enctype设置为text/plain然后通过隐藏的textarea来提交JSON数据。或者使用fetch发起一个“简单请求”满足某些条件如Content-Type为text/plain的POST请求。Content-Type 绕过如果服务器只检查Content-Type头部是否为application/json但实际解析时又兼容其他格式攻击者可能通过表单提交来伪造请求。结合其他漏洞例如如果网站存在XSS漏洞攻击者可以直接在站内注入脚本发出任意请求这比传统的CSRF更强大因为它绕过了同源限制可以读取响应甚至获取Token。这种组合拳危害极大。4. 攻击的投递与诱骗场景实录武器造好了怎么送到受害者手里并让他“开火”呢攻击者的想象力在这里非常丰富。4.1 常见诱骗渠道钓鱼邮件与站内信伪装成系统通知、好友消息、奖品领取链接。“您的账号存在异常请点击此链接验证。” 链接指向的就是恶意构造的CSRF页面。论坛与社交网站在帖子、评论区中嵌入恶意图片GET攻击或发布一个吸引人点击的短链接跳转到自动提交表单的页面。恶意广告Malvertising在网络广告联盟中购买广告位投放包含恶意代码的广告。当用户浏览正规网站时广告iframe加载了恶意页面。水坑攻击攻击者入侵一个目标用户群体经常访问的网站如某个行业论坛在其页面中植入CSRF代码。文件与插件诱使用户下载并打开一个本地HTML文件该文件包含攻击代码。由于file://协议发起的请求也会携带对应域的Cookie因此同样有效。4.2 一次模拟攻击实战推演假设我们作为安全研究员在授权测试一个名为“SimpleBlog”的博客系统。我们发现其修改用户密码的功能存在CSRF漏洞。步骤一侦察我们登录自己的测试账号使用浏览器开发者工具或代理工具如Burp Suite抓包观察修改密码的请求。POST /user/change_password HTTP/1.1 Host: blog.test.com Cookie: sessionidabc123... Content-Type: application/x-www-form-urlencoded old_passcurrentPassnew_passHacked123confirm_passHacked123我们发现这个请求只依赖sessionid这个Cookie进行认证请求体中没有任何Token之类的二次验证参数。步骤二构造我们编写一个恶意HTML文件change_pass.html!DOCTYPE html html headtitle有趣的猫猫视频/title/head body style“display:none” form id“csrfForm” action“http://blog.test.com/user/change_password” method“POST” input type“hidden” name“old_pass” value“” / !-- 我们不知道原密码但发现服务器不校验旧密码这是另一个漏洞 -- input type“hidden” name“new_pass” value“HackedByCSRF” / input type“hidden” name“confirm_pass” value“HackedByCSRF” / /form script // 页面加载后立即自动提交表单 document.getElementById(‘csrfForm’).submit(); /script p如果看到此信息说明自动提交失败。/p /body /html步骤三投递与触发我们将这个HTML文件部署在一个我们能控制的服务器上地址为http://evil-server.com/change_pass.html。然后我们通过测试环境的站内信给另一个测试账号扮演受害者发送一条消息“嘿看看这个有趣的链接 http://evil-server.com/change_pass.html ”。受害者登录着blog.test.com点击了这个链接。他的浏览器打开了这个页面页面瞬间自动提交了表单。请求被发往blog.test.com并带上了他有效的sessionidCookie。步骤四结果服务器收到请求验证Cookie有效于是将受害者的密码修改为HackedByCSRF。受害者可能毫无察觉直到下次登录失败。攻击者此时可以用新密码HackedByCSRF登录受害者的账号。注意事项这个推演中我们假设了服务器不校验旧密码这在实际漏洞中可能同时存在。在真实测试中你需要逐一验证每个参数是否必需、是否被严格校验。CSRF漏洞常常与其他逻辑漏洞并存。5. CSRF攻击的防御体系构建透彻理解攻击流程后防御思路就清晰了打破攻击成功的必要条件。核心是让服务器有能力区分“用户自愿发出的请求”和“攻击者伪造的请求”。5.1 同源检测验证请求来源既然攻击请求来自第三方站点那么检查请求的“来源”就是一个直观的思路。检查Origin Header对于POST请求和跨域的复杂请求浏览器会自动添加Origin头部标明请求发起的源协议域名端口。服务器可以校验这个Origin值是否在白名单内通常是自己的域名。检查Referer HeaderReferer是的历史上拼写错了头部会携带请求页面的完整URL。服务器可以检查Referer的域名部分是否属于自己。优点实现相对简单。缺点Referer可能被用户浏览器设置或安全软件屏蔽导致合法请求被拒绝假阳性。在某些场景下如从HTTPS页面链接到HTTP浏览器可能不发送Referer。Origin头部仅存在于跨域请求和POST等请求中对于同域的恶意请求如站内XSS发起的无效。实操心得在实际开发中我通常将Origin检查作为第一道防线。在中间件或过滤器中对于需要保护的请求优先读取并校验Origin头。如果Origin不存在如简单的同域GET请求再降级检查Referer。同时一定要做好错误处理当Referer缺失时需要有合理的策略如记录日志、要求二次验证而不是直接拒绝以免影响正常用户。5.2 Anti-CSRF Token主流且可靠的方案这是目前最主流、最有效的防御手段。其核心思想是在用户会话中放置一个随机、不可预测的令牌Token并在每次提交敏感请求时要求携带该令牌服务器进行比对验证。实现流程用户访问包含表单的页面时如修改密码页面服务器生成一个强随机数的Token如UUID将其存储在用户的服务器端Session中同时将其嵌入到返回页面的表单里作为一个隐藏字段input type“hidden” name“csrf_token” value“random123...”。用户提交表单时这个Token会随着表单数据一起提交到服务器。服务器收到请求后从Session中取出之前存储的Token与请求参数中的Token进行比对。一致则通过不一致或缺失则拒绝请求。为什么有效攻击者构造的恶意页面无法知道这个随机Token的值。因为他无法读取目标站点的页面内容受同源策略限制所以他无法在伪造的请求中填入正确的Token。服务器校验失败攻击请求被阻断。关键实现细节Token的绑定Token应与用户会话Session绑定最好还能与具体的操作或表单ID绑定增强安全性。一次性使用每次提交后应使当前Token失效并生成新的Token。防止Token被重放攻击。安全的存储与传输Token应通过安全CookieSecure,HttpOnly,SameSiteStrict/Lax或直接嵌入页面隐藏域来传输避免被XSS漏洞窃取如果同时存在XSSCSRF Token也会失效。对所有状态修改操作进行保护不仅仅是POST所有能引起状态变化的GET请求虽然不推荐这种设计也应受到保护。5.3 SameSite Cookie属性浏览器级别的防御利器这是近年来非常有效的一种防御方式直接在Cookie层面设置策略。SameSite属性可以控制Cookie在跨站请求时是否被发送。SameSiteStrict最严格。Cookie仅在同站请求即当前页面URL的域与请求目标域一致时发送。这意味着如果用户从邮件点击链接到银行网站在首次加载银行页面时登录Cookie不会被发送用户需要重新登录。虽然安全但用户体验可能受影响。SameSiteLax默认值宽松模式。在跨站请求中仅对安全如HTTPS的顶级导航如点击链接发送Cookie而对子资源请求如图片、iframe、AJAX则不发送。这可以有效阻止大多数CSRF攻击因为CSRF载荷常通过img,form等子资源触发同时保持了用户从外部链接点击进入网站时的登录状态。SameSiteNoneCookie在所有上下文中发送但必须同时设置Secure属性即仅限HTTPS。设置示例在Set-Cookie响应头中Set-Cookie: sessionidabc123; Path/; HttpOnly; Secure; SameSiteLax将关键会话Cookie设置为SameSiteLax或Strict可以极大增加攻击者构造有效CSRF请求的难度。5.4 双重验证与用户交互对于特别敏感的操作如转账、修改核心安全设置强制要求用户进行二次验证是最稳妥的。重新输入密码执行操作前要求用户再次输入登录密码。短信/邮箱验证码向用户注册的手机或邮箱发送一次性验证码要求输入。生物识别在支持的环境下要求指纹或面部识别。这种方式从根本上将“身份验证”与“操作授权”分离。即使CSRF请求发出没有二次验证信息操作也不会被执行。当然这会增加用户操作步骤需权衡安全与体验。6. 实战中的漏洞挖掘与验证技巧了解了攻击与防御如何在实际的Web应用中发现CSRF漏洞呢以下是我在渗透测试和代码审计中常用的方法。6.1 黑盒测试流程目标枚举使用爬虫工具如Burp Suite的爬虫、OWASP ZAP或手动浏览收集所有可能执行状态更改操作的端点。关注表单提交、AJAX API调用、图片加载URL可能带参数、链接点击等。请求分析对每个可疑端点使用代理工具拦截其请求。重点关注请求方法GET请求执行修改操作是高风险信号。认证方式是否仅依赖单个Cookie或Authorization头参数请求参数中是否存在明显的Token、随机数、时间戳等字段这些字段是否可预测或可绕过PoC构造与测试对于无Token请求直接使用工具如Burp Suite的CSRF PoC生成器生成测试HTML页面。对于有Token的请求尝试分析Token的生成规律。是时间戳的哈希是用户ID的加密是否可预测是否在多个会话或请求间重复使用尝试移除Token参数、填入空值、填入其他用户的Token如果可获取观察服务器响应。测试执行准备两个浏览器或两个独立会话一个作为“攻击者”用于构造和托管恶意页面一个作为“受害者”登录目标应用。在“受害者”浏览器中访问“攻击者”构造的恶意页面。观察“受害者”浏览器中目标应用的状态是否被改变如资料被修改或通过代理工具观察恶意请求是否成功被服务器接受并返回成功响应。6.2 常见Token绕过技巧如果目标站点使用了Token但实现有缺陷仍可能存在漏洞Token绑定不严Token只与用户绑定未与具体操作或会话绑定。攻击者可以先登录自己的账号获取一个Token然后用这个Token去构造针对受害者的CSRF载荷。如果服务器只检查Token的有效性而不检查所属用户则攻击成功。Token可预测Token基于时间戳、用户ID等弱熵源生成。攻击者可以预测出受害者在某个时间点的Token值。Token在GET请求中泄露Token通过URL参数传递?csrf_tokenxxx可能通过Referer头部泄露给其他网站。未校验Token服务器代码存在逻辑分支错误在某些条件下如Content-Type不是application/x-www-form-urlencoded时跳过了Token校验。跨方法滥用Token在GET请求中用于获取页面但该Token在后续的POST请求中也被接受且GET请求的Token容易通过Referer泄露。6.3 自动化工具辅助Burp Suite Professional其Scanner模块能自动检测CSRF漏洞。手动测试时“Engagement tools” - “Generate CSRF PoC” 功能不可或缺。OWASP ZAP同样具有自动化的CSRF漏洞扫描功能。浏览器插件如“CSRF Tester”等插件可以方便地修改请求、移除或添加Token字段进行测试。注意事项自动化工具能发现明显的、标准化的漏洞但对于逻辑复杂的Token校验、依赖特定业务流程的CSRF仍需手动分析和测试。永远不要完全依赖工具。7. 从开发到运维的全局防御 checklist防御CSRF不是某个环节的事而需要贯穿整个软件生命周期。这里提供一份从开发到上线的检查清单。7.1 开发阶段[ ]框架选择优先使用内置了成熟CSRF防护机制的现代Web框架如Django内置CsrfViewMiddleware、Spring Security默认启用CSRF保护、LaravelVerifyCsrfToken中间件。并理解其默认实现原理。[ ]Token策略如果自行实现确保Token满足随机性强使用密码学安全的随机数生成器、与用户会话绑定、一次性使用或短有效期、同时存储在Session和请求参数/头中。[ ]关键操作防护对所有能修改数据、状态、执行重要业务的端点POST、PUT、DELETE、PATCH以及不安全的GET强制实施CSRF校验。[ ]Safe Method遵循RESTful规范使用GET请求只进行读操作绝不用于修改。[ ]Cookie设置为会话Cookie及其他认证Cookie设置Secure、HttpOnly和SameSiteLax或Strict属性。[ ]CORS配置如果提供API精确配置CORS策略不要使用通配符*明确指定可信的来源Origin。7.2 测试与审计阶段[ ]渗透测试将CSRF作为必测项使用手动和自动化工具对上述关键操作点进行测试。[ ]代码审计审查所有处理表单和API请求的代码确认CSRF Token的生成、存储、传递、校验逻辑完整且无缺陷。特别注意是否有接口漏掉了防护。[ ]依赖检查确保所使用的框架和库已更新到最新版本修复了已知的CSRF相关安全漏洞。7.3 运维与监控阶段[ ]WAF规则在Web应用防火墙WAF中启用CSRF防护规则作为一道额外的防线。[ ]日志监控在应用日志中记录CSRF校验失败的请求包括来源IP、请求头如Origin、Referer、目标端点等。异常的校验失败增多可能预示着扫描或攻击尝试。[ ]安全头部署确保正确配置了安全相关的HTTP响应头如Strict-Transport-SecurityHSTS强制HTTPS间接提升安全性。7.4 针对特定架构的考量单页应用SPASPA通过AJAX与后端API通信。Token可以通过初始页面加载时由后端注入到HTML中或者通过一个安全的Cookie发送需确保前端JS能读取到通常不能是HttpOnly然后在每次AJAX请求时由前端JS将其作为自定义头部如X-CSRF-Token附加到请求中。后端同时校验Cookie中的Token和头部中的Token是否匹配“双Cookie提交”的一种变体。API优先架构对于纯API服务可以考虑使用OAuth 2.0、JWT等基于Token的认证并在Token中携带请求来源等信息。但要注意如果JWT存储在Cookie中同样需要防范CSRF。一个常见做法是将JWT放在Authorization头中而前端通过localStorage/sessionStorage存储但这又需防范XSS。防御CSRF是一场攻防博弈。最有效的方式是采用“深度防御”策略不依赖单一机制。结合使用SameSite Cookie、Anti-CSRF Token并对核心操作要求二次验证能构建起足够坚固的防线。每一次对CSRF攻击流程的深入剖析都是为了在代码写下第一行时就将安全的基因注入其中。