CSRF与SSRF靶场实战:原理对比、通解套路与防御修复清单 把CSRF和SSRF放在同一个靶场里连着刷完我最大的感受是这两个漏洞名字看着像亲戚原理却刚好站在“请求信任”的两端。这个好靶场的CSRF与SSRF模块题目数量不算多但每一关都还原了真实业务里最容易忽略的接口很适合已经从“听过漏洞名”走到“能看懂数据包”这一步的人。这篇文章我会先说明为什么这两个漏洞值得对着练再分别拆开CSRF和SSRF的本质、题型和通用解法最后补一节“两者怎么串起来利用”和一份能直接拿去自查的修复清单。基础要求不高只要你会看HTTP请求、知道Cookie是怎么带上的就能跟上等刷到JSON型CSRF和内网绕过那几关你会发现前面的判断流程全都还能复用。1. 为什么CSRF和SSRF适合放在一起练1.1 两个漏洞的“信任方向”正好相反CSRF跨站请求伪造的核心是浏览器会把你的身份凭证Cookie自动贴到每一次请求上不管这次请求是你主动发起的还是某个恶意页面悄悄发起的。服务器看到“带着有效Cookie的修改请求”就默认是本人操作。这里的信任对象是“会话身份”。SSRF服务端请求伪造的核心是服务器因为业务需要去访问用户提供的某个地址相当于把客户端的手搭在服务端的手上让服务器替你向外发请求。大多数应用对“从服务器出发的请求”天然放松警惕因为默认来源可信。这里的信任对象是“请求的发起方”。打个比方CSRF像有人趁你走神拿你的门禁卡去刷了公司大门SSRF则像你通过前台转接电话让前台帮你拨一个内部分机号。前者骗的是“你带着卡”后者骗的是“你是内部电话”。把这两个场景放进同一张对比表区别就很直观了对比项CSRFSSRF请求发起方受害者的浏览器存在漏洞的服务器攻击者控制位置跨站页面里拼出来的请求服务端某个“URL参数”核心信任点“带Cookie本人本意”“服务器发出的请求内部可信”能影响的范围受害者已登录的业务操作内网服务、本机接口、文件系统典型关键词改资料、改密、转账、绑定图片下载、URL预览、导入、回调1.2 靶场题目的两条线索好的靶场不会把漏洞出成脑筋急转弯它会按真实业务埋点。CSRF的关卡基本都长在“改资料、改密码、绑定手机号、支付接口”这些状态变更操作上防护要么完全没有要么故意留了一个看似严密但能被绕过的Token机制SSRF的关卡则围绕“远程图片加载、URL转PDF、RSS导入、Webhook回调”这类需要服务器主动访问外网的接口让你从“参数到底是不是一个URL”这类蛛丝马迹里定位触发点。我刷题时脑子里一直挂着两句话CSRF问的是“这个请求是不是本人本意”SSRF问的是“这个地址是不是允许访问”。带着这两个问题去读代码和数据包比死记题型管用得多。靶场把这两个漏洞放在一起本身就是想让你对比这种“信任错位”——一个信错了身份一个信错了来源。1.3 练习环境怎么搭最顺手开始刷题之前把环境准备好能省下一半的排查时间。我的习惯是这么几件事一台本地虚拟机跑靶场最常见的组合是PHPMySQL那套经典架构。装好以后做一次干净的系统快照往后每个关卡反复复位、反复重刷很省心。一个抓包改包工具没有的话用浏览器开发者工具也行。网络面板看请求、复制为curl、改Cookie重放这几件事在验证CSRF时几乎天天用。两个隔离的浏览器环境一个当受害者始终保持“已登录”状态另一个当攻击者专门打开你构造的恶意页面。两个环境必须隔离Cookie最简单的方式是不同浏览器或者不同用户配置文件。再准备一个HTTP监听服务用来做SSRF的带外观测。最简单的写法是起一个Python脚本把请求路径追加到日志文件import http.server class H(http.server.BaseHTTPRequestHandler): def do_GET(self): open(ssrf.log, a).write(self.path \n) self.send_response(200) self.end_headers() http.server.HTTPServer((0.0.0.0, 8000), H).serve_forever()把所有攻击验证都限制在这台本地靶场虚拟机里不要拿这套思路去没有授权的系统上试这是底线。2. CSRF的本质别把“带着凭证”当成“本人确认”2.1 一句话解释CSRF跨站请求伪造的意思是攻击者利用你在正常站点上的登录状态让浏览器替你发出某些指令。“伪造”这个词有点误导人——攻击者并没有伪造你的身份凭证你的Cookie真实有效他只是伪造了一个“请求场景”你明明只是在看一个网页就已经顺手提交了转账、改密或发布操作。理解这个点很重要因为它决定了CSRF和XSS的边界。XSS是在目标站页面里执行脚本等于抢到了钥匙CSRF是在你自己的页面里遥控浏览器属于“借”钥匙不是“抢”钥匙。靶场里很多题区分两者看的是脚本能否在目标域下执行还是单纯只需要浏览器发起一个跨站请求。2.2 三个先决条件少一个都不成立第一目标操作必须是会改变状态的请求。查询数据、读取页面的GET请求即使被跨站触发通常也只是信息泄露谈不上“伪造”真正危险的是改密码、改绑定、发帖、转账这类有副作用的请求。第二这个请求的凭证由浏览器自动携带。Cookie是最常见的也包括某些自动附加的Authorization头。正因为浏览器自动带上凭证攻击者才能隔空借你的身份。如果凭证是攻击者拿不到的、并且请求必须手动附加的CSRF自然就不成立。第三服务端没有校验“请求意图”。缺少Token、对Origin不检查、Cookie没配SameSite都是典型的缺口。靶场题目千变万化本质上都是在找这个“三缺一”的缺口位置。2.3 四种最常见的出题款式GET型状态修改参数直接放在URL里常见于改昵称、退订、标记已读这种开发图省事的接口。跨站触发最简单一个img、一个诱导点击的链接就够。有些靶场把它包装成“下载报告”或“切换语言”但只要判断依据是“GET且修改了数据”就直接进入CSRF考虑范围。POST型正常表单请求Content-Type是application/x-www-form-urlencoded。跨站页面可以用一个自动提交的form完整模拟出同样的请求行为上没有任何差别。这是最经典的题型也是我建议所有刷题者必须先打通的关卡。JSON型接口接收application/json前端用AJAX提交。跨站普通表单模拟不了JSON的Content-Type但有个老技巧能用enctypetext/plain的表单拼出合法JSON。靶场出到这一关基本就是让你理解“表单编码和JSON解析之间的缝隙”form actionhttps://target.lab/api/change_profile methodPOST enctypetext/plain textarea name{email:attackertest.lab,csrf:}/textarea /form scriptdocument.forms[0].submit();/script这段代码的重点在textarea的name值name是左半个JSONtextarea内容是右半个JSON表单编码时中间恰好补了一个“”于是提交出去的body就是{email:attackertest.lab,csrf:}一个合法的JSON对象。服务端只要没在拿到JSON后重新校验token来源就会把它当成正常请求处理。Token形同虚设型接口里确实有CSRF Token但它是写死的、复用的、放在URL里的、校验时允许为空或者Token不跟会话绑定。靶场里最典型的一种是“攻击者自己注册一个账号拿到合法Token同样能在受害者请求里通过校验”因为Token没有绑定到具体会话。遇到这类题先验证Token是不是每个会话唯一再验证它是不是真的被服务端校验了。2.4 SameSite出现后这些题要重新看一遍现代浏览器的SameSite属性已经把一部分CSRF天然挡掉了。SameSiteLax时img、fetch这类子资源跨站请求不会带Cookie但用户通过点击链接触发的顶层GET导航仍会带CookieSameSiteStrict时更苛刻跨站场景基本不带。这个属性对靶场的影响是明显的以前靠img秒杀的GET型CSRF在Lax下可能翻车但POST型CSRF本来就不是顶层导航Lax下基本带不了Cookie所以防护效果更明显。刷题时要在抓包里确认Cookie的SameSite值别凭经验脑补。还有一类服务端会用Referer或Origin做校验常见坑是只查“字符串里是否包含目标域名”。攻击者可以用http://evil.com/?target.lab这种URL让Referer里包含目标域名或者通过meta namereferrer contentno-referrer让Referer整个为空而部分实现又会对空Referer放行。靶场关卡往往故意留这种缝正好说明“单点防护为什么不可靠”。3. CSRF关卡的通解套路从看请求到验证触发3.1 先把操作翻译成请求拿到题先别急着找漏洞用抓包工具把目标操作完整录一遍。记录四件事请求方法、请求路径、提交的参数、请求携带凭证的方式。如果你发现某个“敏感操作”用的是GET直接把这条记进可疑名单。参数里有没有Token、Cookie里的会话结构是什么这些信息后面会反复用到。这一步最容易忽略的是“并发请求”。有些靶场会把Token的校验设计成“一次性使用”但业务接口会同时发出两个请求比如先保存草稿再提交此时只要提前把一个请求里的Token重放到另一个请求里就有可能在边角料里找到绕过窗口。先记录完整的请求顺序再谈构造。3.2 按防护逐项打勾接下来依次确认几件事请求里有没有一个像随机字符串的Token字段它的值是不是和当前会话对应服务端有没有校验Origin或Referer校验规则是严格比对域名还是只查字符串包含Cookie的SameSite属性是什么接口是否要求自定义请求头。把这四项的状态写在纸上漏洞成不成立基本就清楚了。服务端防护做得越“杂”越容易在某个实现细节上出错——比如Origin校验只查了http://开头或者只查了端口之前的部分这些都会在后续验证时成为突破口。3.3 根据请求类型选择PoC模板判断完防护之后按照请求长相套模板请求长得像PoC方案GET且能改状态img触发或诱导点击POST表单自动提交的form接收JSONtext/plain拼JSON需要完整交互流程把多个步骤做成连续页面脚本依次提交GET型最朴素的写法img srchttp://target.lab/user/delete?id1024 width0 height0POST型最常用的写法form actionhttp://target.lab/user/change_email methodPOST input typehidden nameemail valuestealtest.lab /form scriptdocument.forms[0].submit()/script隐藏域和自动提交脚本是为了减少用户操作。有的靶场故意要求模拟“用户点击”因为真实场景中诱导点击更隐蔽触发方式要从“用户会点什么”来选。比如把PoC放在一个下载按钮上、一段迷惑性文案后面比页面加载就弹alert更像真实攻击链。3.4 双浏览器验证是最后一步准备两个隔离的浏览器环境。环境A登录目标站模拟受害者把会话保持在有效状态环境B不登录目标站只负责打开攻击页面。受害者环境打开攻击页之后立刻切回环境A检查目标操作是否已经发生。之所以要隔离是因为同一个浏览器里同时存在受害者Cookie和攻击页面会出现“SameSite判定正确但Origin是同一站点”的干扰导致判断错误。隔离之后受害者身份、攻击者页面、目标站点三个角色清清楚楚验证结果不会糊。3.5 这类题最容易踩的三个坑一是把CSRF和XSS混为一谈。XSS是在目标页面里执行脚本CSRF是跨站伪造请求。如果接口同时存在XSS和CSRF往往是XSS能直接模拟请求CSRF反而成了锦上添花修复时要分开看待。二是验证时只在攻击者的未登录环境里看结果。CSRF的前提是受害者已登录所有测试都要以受害者视角为准。你很可能在“未登录环境测试失败”之后误判漏洞不存在而切换受害者视角立刻复现。三是盯着表单忘记了Cookie。有些操作需要请求里带上某个独立的Cookie才生效仅跨站提交表单是不够的。如果这个Cookie是第三方可设置的那就是另一个独立漏洞点不要混进CSRF结论里。4. SSRF的本质服务端的手被借到内网去敲门4.1 SSRF到底在放什么进来SSRF的经典定义是服务端根据用户可控的URL发起请求。这里的关键词不是“URL”而是“服务端发起”。攻击者平时只能从外网访问你的服务器但服务器自己是可以访问内网、访问自己回环接口的。一旦某个业务接口允许用户指定一个地址并让服务器去访问攻击者就等于搭上了服务器的内网通道。客户端和服务器之间通常隔着一层安全边界但服务器到内网往往没有同等的访问控制。很多内网接口、管理后台直接绑在非公网地址上外网扫不到却挡不住来自服务器自身的连接。这就是SSRF能造成破坏的根本原因。4.2 哪些接口是重灾区业务场景用户可控的部分常见参数名图片缩放/下载图片URLurl、img、pic网页快照/转PDF待转URLlink、target_urlRSS/Feed导入订阅地址rss、feedWebhook回调通知回调地址callback、webhook资产探测/URL健康检查检查目标host、url远程文件导入下载地址file、source识别方法不是看参数名而是看行为。一个“传URL自动下载”的头像功能就是SSRF触发点只能传文件本体的上传接口则不是。先问自己这个操作会不会让服务器额外发起一次网络请求会就进入SSRF排查流程。4.3 能打到哪从内网探测到元数据最基础的利用是内网探测。把URL改成http://内网IP/通过返回内容、超时、错误页差异判断端口和服务再进一步是访问本机和管理接口很多开发环境把调试面板、性能面板、管理后台绑在127.0.0.1外网打不到但SSRF可以。云上的应用还有一个经典目标——云元数据服务。它通常监听在169.254.169.254这类链路本地地址里面放着实例ID、网络配置甚至角色凭证等敏感信息。如果应用部署在容器或虚拟机里SSRF打通这个地址往往比打内网一台普通机器值钱得多。如果协议没限制file://能读本地文件dict://、gopher://则可以跟内网里基于TCP的服务交互。后面这两种属于进阶玩法靶场里一般出现在最后几关原理大于实操。但至少要知道SSRF能影响的绝不是“多访问一个网页”那么简单。5. SSRF的通解步骤与绕过组合拳5.1 第一步确认它真的在“替你请求”先把可疑参数改成指向自己可控地址的URL。可控地址怎么来本地靶场里最容易的是配前面写的那个HTTP监听器监听一台你自己能访问到的机器或虚拟机的8000端口。把参数改成http://监听机IP:8000/ssrf_test请求一进来日志里就有记录。没有回显的接口也能靠这个确认触发点。这一步要留意“是否带外验证失败就排除了SSRF”。有些应用会对URL做一次DNS预解析或连接复用第一次请求没打到监听器可能只是缓存或重试逻辑问题多触发几次、换个URL参数位置再下结论。5.2 第二步摸清协议与过滤规则先测协议。http://能通不代表file://能通分别试一下file:///etc/passwd、dict://127.0.0.1:6379/info观察报错和响应差异就能摸出服务端到底放行了哪些协议。再测过滤。把127.0.0.1的各种写法逐个丢进去看哪个被拦、哪个放行。重点观察过滤代码是针对“原始字符串”做黑名单还是针对“解析后的主机名/IP”做校验还是两层都做。这个判断决定了后面用哪套绕过组合。5.3 绕过字典一次看明白“为什么拦不住”绕过思路示例说明点分十进制http://2130706433/127.0.0.1的十进制整数写法十六进制http://0x7f000001/127.0.0.1的十六进制写法八进制http://0177.0.0.1/127.0.0.1的八进制写法IPv6http://[::1]/回环地址的IPv6表示IPv4映射IPv6http://[::ffff:127.0.0.1]/兼容IPv4的IPv6地址符号混淆http://test.lab127.0.0.1/实际请求目标是127.0.0.1#或?干扰http://127.0.0.1#test.lab后面内容只是fragment或query自定义域名解析到内网自己控制一个域名指向127.0.0.1域名白名单被绕过跳转短链或自建跳转页302到内网请求库跟随重定向时不重新校验DNS重绑定第一次解析外网IP通过校验第二次解析内网IP校验和实际连接不是同一次解析URL编码对部分字符做URL编码检测和解码顺序不一致会被绕过http://evil.com127.0.0.1/这个写法值得单独说说URL里真正的主机是127.0.0.1evil.com只是userinfo部分。黑名单如果只检查“URL里不能出现内网IP”却不解析URL结构就会错误放行。#的干扰同理http://127.0.0.1#evil.com的目标地址是127.0.0.1evil.com只是fragment字符串检查却可能认为域名是evil.com。还有一个“双URL写法”http://127.0.0.1/http://evil.com在部分实现里解析器和实际请求库取的是不同一段导致校验目标与实际请求目标不一致。这种解析差异在靶场里属于必试项因为每换一种后端语言结果可能都不一样。5.4 依赖解析结果的绕过重定向与DNS重绑定很多防护只校验传入的域名认为域名安全就直接发请求。这时有两个绕法。一是短链或自建跳转页做302到内网地址。如果请求库跟随重定向且不重新校验马上绕过。二是DNS重绑定让同一个域名第一次解析给一个外网IP通过校验发起实际连接前再解析成内网IP。两者本质都一样“校验时看到的”和“连接时访问的”不是同一个目标。在靶场里模拟DNS重绑定最简单的方式是改本地hosts或者用自有域名改DNS记录把校验和访问拆开触发。理解这个原理比亲手复现更关键——很多生产环境的SSRF防护死就死在“校验一次就以为天下太平”上。5.5 没有回显时怎么判断连通性看时间请求到开放端口可能立即返回或响应偏慢到关闭端口会快速失败或超时多组对比能推测端口状态。看错误很多应用会把上游错误原样带出来“Connection refused”和“Connection timeout”的区别就是信息。看带外让目标反过来请求你的监听器或者触发一次对自有域名的DNS解析用日志确认连通。通解建议是永远不要只依赖一种回显方式。靶场里有些SSRF关卡设计成“无回显”其实就是逼你学会用时间和带外交互去推理。这个能力比单纯会背绕过写法值钱。6. CSRF与SSRF的联动一个容易被漏掉的真实组合6.1 为什么它们能连起来很多接口是登录后才能使用的包括SSRF触发点。攻击者没有受害者会话本来调不动这个接口。但CSRF恰好不需要攻击者持有会话——它只需要受害者的浏览器带着受害者自己的会话去访问这个接口。于是两条漏洞线接上了攻击者先在受害者身份下触发一次跨站请求让服务器去请求攻击者指定的内网地址。这在真实业务里的对应场景很常见开发团队给某个“URL下载接口”做了登录校验以为外网用户不能直接用但他们忘了给接口配CSRF Token。做SSRF防御时只加了一层“要登录”相当于给门上了个门把手门还是没锁。6.2 组合关的观察顺序找服务端会主动发请求的功能图片下载、URL预览、Webhook回调确认它是否要求登录如果要求登录继续查有没有Token、Origin、SameSite防护如果只有登录校验把它当成一个CSRF目标构造跨站页面触发它触发成功后再走SSRF的绕过流程去打内网目标。这种组合题在靶场里通常被放在“进阶”区。做出来的瞬间你会觉得CSRF和SSRF不是两块独立知识它们本来就串在一条请求链路上CSRF控制“谁发起”SSRF控制“发到哪”。两层都防住链子才断。6.3 倒过来的组合也存在反过来同样成立SSRF访问到内网某个未授权接口后如果这个内网接口本身也缺少CSRF防护攻击者相当于借用服务器在内网网络里的位置替自己发了一轮“跨站请求”。理解了“谁借谁的凭证发起请求”这个本质链式场景一眼就能看穿后续刷题遇到SSRF打内网Redis、内网管理台这类关卡思路也会顺很多。7. 刷完之后照着改两份防御清单7.1 CSRF修复自查[ ] 状态变更请求一律限制为POST避免GET改数据[ ] 每个会话独立生成随机Token提交时与Session校验使用后失效[ ] Token放在请求体或自定义Header里不放URL[ ] 关键接口要求自定义Header跨站表单发不了[ ] Cookie配置SameSite根据业务在Lax和Strict之间选择[ ] 不要只依赖单一校验Token为主、Origin校验兜底[ ] 登录、登出、改密码这类边界接口也要覆盖。登录接口本身也存在CSRF这一点经常被忽略。攻击者可以诱导受害者登录攻击者的账号受害者后续做的每一步操作都会被记录变成另一种隐私泄露。靶场里如果单独出了一道“登录CSRF”的题它修起来和普通CSRF没有本质区别但容易被漏。7.2 SSRF修复自查[ ] 协议白名单只放http、https[ ] 目标域名白名单不靠IP黑名单[ ] 域名解析后的IP在连接前再次校验排除回环、私网、链路本地地址127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16、169.254.0.0/16、::1、0.0.0.0都要考虑[ ] 禁止跟随重定向或每次重定向都重新校验[ ] 用独立的DNS解析并单独校验解析结果防DNS重绑定[ ] 出网只开放必要端口响应内容脱敏错误不原样回显设置超时[ ] 在URL解析和HTTP连接这两个层面都拒绝非http协议。7.3 三个最常见的防御误区一是黑名单思维。禁止127.0.0.1、禁止localhost是典型的黑名单进制写法、IPv6、重定向、DNS重绑定全都能绕。防御只能靠白名单加解析后校验。二是以为SSRF是输入过滤问题。SSRF的根子在“出网请求不受控”输入过滤只是补丁。修复要落在网络出口、协议白名单、请求库配置上而不是在参数层面反复加规则。三是“有Token就安全了”的错觉。Token解决的是“请求是不是用户本意”解决不了“服务器访问了不该访问的地址”。CSRF和SSRF是两条信任链各修各的不能混用。收尾把“谁替谁发请求”刻进脑子里这套靶场刷完两轮之后我养成了一个习惯凡是看到服务器会主动访问外部地址的功能先在纸上画一条请求链路标出“谁发起的”“带谁的身份”“访问的地址可不可控”。CSRF和SSRF表面上一横一竖落到这条链路上其实就是两个节点的事。建议你把每一个关卡重置之后不看笔记再走一遍再试着把漏洞的修复代码写出来。能动手修才算真正通解。