news 2026/10/22 23:59:19

SSRF服务端请求伪造:原理、利用与防御实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SSRF服务端请求伪造:原理、利用与防御实践

先说一个我自己的经历。某次线上排查,发现一台业务服务器在半夜不断向内网发起非常规的请求,抓包一看,目标端口从 3306 扫到 6379,再扫到 11211。顺着调用链往上追,问题落在了我们自己做的一个“URL预览”功能上——前端传一个链接,后端为了生成预览信息,直接拿这个链接去请求。这其实就是一场典型的服务端请求伪造,也就是 Security 圈里常说的 SSRF。

SSRF 的全称是 Server-Side Request Forgery,中文一般叫“服务端请求伪造”。很多刚入门的朋友一听这个名字就觉得高深,其实它表达的意思非常直白:服务端接收了一个攻击者可控的 URL,然后以服务端自己的身份去请求这个地址。关键在于“服务端发起的请求”,而不是用户的浏览器发起的请求。浏览器发出的请求只能访问公网资源,但服务端发出的请求,往往可以够到内网里那些根本不对外暴露的主机和服务。

这篇内容我会从零开始拆这个漏洞。适合三类人看:一是刚接触 Web 安全、想系统理解漏洞原理的初学者;二是写后端接口、担心自己功能被利用的开发者;三是做安全测试或线上排查、需要快速定位问题的人。整篇不涉及高深数学,也不需要你提前掌握多少协议知识,只要你写过接口、调过接口就能看懂。

1. SSRF 的成因拆解:服务端为什么会“帮”攻击者发请求

1.1 从一次正常请求说起

要想理解 SSRF,先得弄明白普通网络请求是怎么流动的。假设你在浏览器里访问一个公开页面,请求是你发出去的,服务器收到后把页面返给你。整个过程里,发起请求的是你的浏览器终端,IP 是你的 IP,服务器只负责响应。

现在换个场景。你写了一个后端接口,它要做一件事:从某个 URL 获取 JSON 数据,解析之后存到数据库。这时候你自己的服务器就变成了“请求者”。服务端代码里写了类似“去访问这个地址并读取内容”的逻辑,而这个地址可能是写死在配置里,也可能来自用户输入。

SSRF 之所以成立,需要有三个要素同时满足:

  • 服务端代码中存在“主动发起网络请求”的逻辑;
  • 目标地址由外部输入控制,而不是固定写在代码里;
  • 服务端对输入的目标地址没有做足够严格的限制。

当这三个条件凑齐,攻击者输入一个内网地址(比如 192.168.x.x),服务端同样会去请求它。因为请求是从服务端发出的,内网会认为这是可信来源,于是把本不该暴露的数据或服务响应返回给服务端。这就是“伪造”的含义——伪造的是发送方身份,攻击者借了服务器的名义去探访内部资源。

1.2 漏洞的核心条件:输入可控加请求触发

很多初学者会把 SSRF 和普通的“URL 跳转”混淆。URL 跳转是服务端返回一个 302 让浏览器跳转到指定地址,跳转动作发生在浏览器端。SSRF 则是服务端自己拿着 URL 去请求,动作发生在服务端。两者最大的差别就是“谁在执行请求”。

从触发方式上看,SSRF 往往藏在一些看起来很普通的功能里。比如:

  • 上传图片时填写一个图片 URL,前端展示前需要后台拉取;
  • 提交一个分享链接,后端要抓取网页标题、描述、缩略图;
  • Webhook 回调地址由用户填写,触发事件后服务端去回调;
  • 服务器需要代理请求外部接口,而代理地址直接取参数值。

这些功能表面上是“为了用户方便”,但都共同指向一个后端行为:按用户提供的地址,由服务器去访问它。只要地址可控,内网就会暴露在这个请求半径之内。

1.3 最容易写出 SSRF 的代码长什么样

来看一段很典型的伪代码,用 PHP 的 file_get_contents 实现远程拉取内容:

// example: /fetch?url=https://example.com/robots.txt $url = $_GET['url']; // 用户可控 $content = file_get_contents($url); // 服务端直接请求 echo $content;

这段代码的逻辑非常清晰:从参数取 url,请求它,然后把响应当作页面内容返回。功能上没毛病,可是攻击者只要把参数从https://example.com/robots.txt换成http://127.0.0.1:3306,服务端就会尝试连接本地数据库端口。如果换成内网某台主机,就相当于以服务端为跳板,向内网发起访问。

Python 场景也很常见,用 requests 库的话问题更隐蔽:

# example: /proxy?url=http://api.example.com/data from flask import request import requests def proxy(): target = request.args.get('url') resp = requests.get(target, timeout=5) return resp.text

代码写得没问题,从功能角度它确实是个现成的代理。但这正是问题所在:任何用户可以来用,任何地址都可以填,目标端口、目标协议都不受限。

Java 里 HttpClient、Go 里的 http.Get,本质都一样。凡是把外部输入直接拼进请求目标的操作,基本都是高危写法。代码审计时看到这一类写法,就要打起精神问一句:这个输入真的可控吗?输入做了什么限制吗?即使没有直接回显,服务端发起请求本身也可能被用来探测端口、标识可用服务。

2. 业务场景与危害面:先知道哪些功能要重点留意

2.1 高危功能入口盘点

把所有可能出现 SSRF 的功能点列一遍,你在排查代码时就能按图索骥。我自己整理的高频区域有这些:

功能类型典型实现风险说明
图片加载/抓取传入图片 URL 后服务器下载并转存可探测内网 HTTP 服务、读取文件
链接预览/分享卡片抓取标题、描述、封面最常见的 SSRF 入口,开发最常忽略
文档导入/转换通过 URL 导入文件再转 PDF 或图片可配合跨协议扩展为读取本地文件
Webhook 回调用户填回调地址,后端事件触发时 POST 数据地址完全可控,且能携带内部数据
接口代理/转发后端转发请求到前端指定的地址等于是送了一个可控代理
文件下载服务用户填地址,后端下载到本地内网文件甚至云元数据都可以访问

这些功能出现在后台管理、内容发布、用户个性化配置等场景里。尤其是后台管理系统,因为访问者少,开发时往往默认“公司内部人使用”,限制做得更松。可一旦攻击者通过其他漏洞进入后台,这些 SSRF 入口就成了横向移动的跳板。

2.2 SSRF 与 CSRF:经常被搞混的两个“请求伪造”

初学阶段容易把 SSRF 和 CSRF 放在一起记,因为他们名字里都有“RF”——Request Forgery。但两者的含义可以差出十万八千里。

维度SSRFCSRF
发起方服务端受害用户的浏览器
攻击者控制点URL/目标地址伪造整个请求动作
主要影响内网信息泄露、服务探测以用户身份执行操作
请求携带的身份服务端自身的权限用户的登录态
典型场景图片抓取、链接预览更改密码、转账、改邮箱

一句话总结:CSRF 是“冒用用户身份”,SSRF 是“借用服务器能力”。两者都需要重点防护,但思考的方向完全不同。我在排查项目时,遇到“请求伪造”相关的问题,第一步都是先判断请求是发生在哪个上下文环境里,再判断该往哪个方向排查。

2.3 从请求者的角度理解影响面

理解 SSRF 的影响面,关键要理解一个概念:服务端发起请求时携带的是服务器自己的 IP 和权限。外网攻击者平时到不了内网,但服务器就生存在内网。攻击者能让服务器替他访问内网的任何地址,就等于给服务器装了一双“遥控的眼睛”。

这双眼睛能看到什么,取决于服务端放在哪、能拿到什么权限。如果服务器能访问数据库,那攻击者可以通过探测确认 3306 端口是否开放;如果服务器在云环境里,访问某个内部元数据地址可能拿到密钥和临时凭证;如果服务端能访问本地文件,跨协议读取可以让任意文件内容进入响应体。这些单看任何一个好像都还行,串起来就是一个完整的攻击链。

3. 利用视角下的深层危害

3.1 从一个端口扫描的角度理解探测行为

攻击者拿到一个 SSRF 漏洞后,第一件事往往不是直接打内网应用,而是先做信息收集。怎么收集?靠的是请求时间差和返回内容差异。

比如用 SSRF 访问http://127.0.0.1:8080和访问http://127.0.0.1:81,响应时间、报错提示、页面内容都会有细微区别。端口开放时连接建立得快,端口不通会超时或连接失败;有些服务会暴露 banner 信息,返回协议头部。攻击者把这些结果记录下来,就能在几十分钟之内摸清内网一段 IP 里哪些主机活着、哪些端口开放、运行着什么服务。

这种探测最大的特点是隐蔽。从攻击者的角度来看,请求的源 IP 是受攻击服务器本身的 IP,不是攻击者的 IP。内网日志里看到的是一台业务服务器在“正常地”访问其他内网主机,没有明显的异常终端标识。这比攻击者直接向内网发包要隐蔽得多。

3.2 跨协议请求:不只是 HTTP

SSRF 经常被误解为“只能探测内网的 HTTP 服务”。事实上,服务端发起的请求完全可以携带其他协议。PHP 的 file_get_contents/curl、Python 的 requests、Java 的 URLConnection,在开启允许协议的配置下,可以访问 file、gopher、dict、ftp 等协议。

这里我重点说两个协议,理解这两个协议的价值,就能理解跨协议攻击的严重性。

第一个是 file 协议。当后端允许 file 协议时,攻击者可以直接通过 URLfile:///etc/passwd读取服务器本地文件内容。如果响应内容会回显,那你等于送出了一个任意文件读取漏洞。即使不回显,攻击者也可以用盲读的方式探测文件的存在性。

第二个是 gopher 协议。gopher 协议的核心特点是允许客户端构造任意字节流发送给服务端,这就意味着攻击者可以“模拟”一次完整的 TCP 会话——比如向本地的 Redis 发送命令。虽然现代 Redis 默认配置已经不允许非认证访问,但在内网环境里仍存在配置不当的老实例。一旦打出这一步,就从网络层探测升级到了代码执行级攻击。

需要注意:不是所有后端语言都默认支持这些协议,很多拦截策略也能限制协议类型。但确认协议支持情况之前,攻击者只要多试几个前缀,就能知道后端到底放开了多少能力。防御者在设计时不能只盯着 HTTP,必须假设所有协议都可能被调用。

3.3 为什么这些危害对业务是致命的

把上面几点串起来,一个完整的 SSRF 攻击路径大约是这样的:先发现一个可控 URL 的接口,然后探测内网存活主机与端口,接着发现某个老的内部应用或数据库,再利用跨协议读取文件或写入命令,最后撬动整片内网。

这里面每一步都不需要极高深的技巧,真正困难的反而是“能不能发现第一个入口”。一旦入口被确认,后边的操作非常机械化。这也是为什么现在的一些安全评审会把 SSRF 的严重级别定得和 SQL 注入差不多——单看漏洞本身只是“能发请求”,但结合内网环境和业务逻辑,它可以演变成不同层级的真实入侵。

4. 防御设计:为什么很多限制方案会被绕过

4.1 常见的限制手段

说到 SSRF 防御,大家第一反应通常是“过滤内网 IP”。这个想法没错,但实现起来远比想象中复杂。常见的防护措施有:

  • 限制请求协议只能是 HTTPS/HTTP;
  • 对目标地址做主机名白名单校验;
  • 对解析后的 IP 做内网地址黑名单限制;
  • 禁止请求跳转,或限制跳转次数;
  • 通过代理网关统一出网,业务层不直接发外部请求。

这些方案单独看都有道理,但组合起来才有效。真正的问题是:很多项目只做了其中一条,而且校验逻辑本身存在细节漏洞。下面重点说说这些“细节漏洞”长什么样。

4.2 绕过思路的技术根源

先说一个冷知识:同样是“127.0.0.1”,可以写成很多不同形式。比如十进制整数形式、十六进制形式、短横线形式、IPv6 映射形式。如果后端只做字符串过滤,判断“字符串中是否包含 192.168”,那攻击者只要换一种表示形式就绕过了。

解析差异也是一个大坑。有的方案在进入请求前先做一次域名解析,解析出 IP,然后拿这个 IP 做内网判断。但内部客户端请求时,往往会再做一次解析。如果两次解析的结果不一样——也就是所谓的 DNS 重绑定攻击——那你校验时看到的是公网 IP,真正请求时拿到的却是内网 IP。既然校验和实际执行用的是两次解析结果,那中间就是天然的绕过空间。

还有跳转处理。很多开发者想到了限制跳转,但方案是“只允许一次跳转”。攻击者构造一个公网地址,这个公网地址再 302 跳转到内网地址。第一次校验通过了,跳转后就没有第二次校验了。这种绕过实现起来非常简单,根本不用理解复杂原理,试一次就能成功。

再一个常见坑是 IP 判断不全面。只过滤了 IPv4 的内网地址段,但没过滤 IPv6 的内网地址段,或者没过滤本机回环地址的各种别名形式。攻击者的想象力远比防御者的规则表丰富,漏一种就相当于没防御。

4.3 一套相对可靠的防御方案

踩过不少坑之后,我现在比较推荐这套组合策略:

  1. 协议白名单:只允许 HTTP 和 HTTPS,其他协议一律当场拒绝。实现上用标准库解析 URL 的 scheme 部分,不要用简单的是否包含判断。

  2. 域名解析与 IP 多重校验:拿到目标地址后,先解析域名得到 IP 集合,对这个集合做内网地址判断。内网地址段要覆盖 IPv4 和 IPv6 的常见保留段,包括回环、链路本地、私有地址、保留地址等。

  3. 请求解析与实际请求拆分:DNS 解析结果只作为检查用,实际请求时用一个代理商层代理或网关,让网关再校验一次出口目标。两层都允许才放行,从根本上规避 DNS 重绑定。

  4. 禁止跳转:客户端库里的跟随跳转能力全部关闭。如果需要支持跳转,就在每一跳之后重新执行校验,绝不信任上一次的校验结果。

  5. 统一出网:构建一个独立代理或出网网关,业务服务器不直接访问外部,所有外联请求必须经过网关。网关配置目标白名单,未命中的请求直接拒绝。

这套方案不能说是绝对无法绕过,但它把需要攻击者同时利用的漏洞点提升到了多个。想绕过,得同时处理协议问题、解析差异、跳转处理再穿越网关,难度就不是单纯字符串过滤能比的。

5. 检测、排查与代码审计要点

5.1 日志侧怎么发现异常

平时做线上巡检,日志是最容易发现 SSRF 的入口。需要重点关注几类日志:

  • 服务器对外发起请求的 IP 和端口记录,同一来源短时间内大量访问不同目标的端口,高度可疑;
  • 带有 url、target、callback 等参数名的请求日志,如果访问目标里出现了内网地址段,基本可以判定存在问题;
  • 业务日志中同一会话内出现高频连接失败记录,可能是在探测端口。

有一说一,纯日志检测对 SSRF 的覆盖效果有限,因为正常的业务外联也会产生大量日志。更靠谱的方式是网络侧监控做流量画像,或者直接做代码审计,从源头发现问题。日志更适合用于“发现了异常之后回溯攻击路径”,而不是作为第一道防线。

5.2 代码审计时要盯住的关键函数

代码审计时我一般从哪些函数看起?不同语言有不同的关注列表:

  • PHP:file_get_contents、curl_exec、fopen、readfile
  • Python:requests.get/post、urlopen、httpx 调用
  • Java:HttpURLConnection、HttpClient.send、OkHttp 请求
  • Go:http.Get、http.Post、net.Dial

只看关键函数还不够,还要追一层调用链。确认传入的参数来源、是否经过过滤、过滤发生在哪一层。有的项目在入口处做了全局过滤,那只需要检查过滤逻辑覆盖了哪些函数调用;有的项目只在部分路由里做了过滤,那其他路由全部是裸奔状态。

我习惯用“谁能控制这里面的参数”来作为出发点。如果参数来源是后端配置文件或环境变量,风险较低;如果参数来源于 GET/POST 参数、请求头或用户上传的内容,就要排查所有可能的校验路径。面试时很多人能背出 SSRF 的定义,但一问“你负责的项目里哪个接口存在这个风险”就卡住了。解决这个问题的唯一办法,就是顺着代码从数据入口一路跟到请求函数。

5.3 常见问题与排查技巧速查

最后整理一个我在日常交流中经常被问到的问题列表,以问答形式给出来,方便快速定位。

问题判断思路处理方向
为什么限制了内网 IP 还是会被打?可能是校验和解析不是同一份结果,也可能是 IP 格式变体或 IPv6 漏配统一解析点,检查所有内网地址段
返回内容看不到任何内部信息,是不是就没危害?即使无回显也可用于端口探测和盲打不能只依据回显判断风险
禁止了 http 协议就能解决吗?如果后端支持其他协议,提出了也未必堵死协议白名单设置在解析层
服务端不支持时只能完全禁用外联吗?不建议禁用,业务往往需要外联用专门的出网代理做白名单管控
开发测试环境能不能放开限制?测试环境的内网设备通常和线上共享环境间安全等级建议保持一致

这几个问题对应的是实际场景里的高频误区。单独记结论不难,难的是遇到具体的业务场景时综合判断。我自己的经验是:把校验逻辑抽象成一个独立的公共组件,而不是散落在每个接口里写一遍。散落的逻辑最容易出偏差,一个接口写一种过滤,最后总有漏网之鱼。

写了这么多,核心还是要回到“为什么”上。SSRF 的成因不复杂,复杂的是它对系统架构的牵动。真正见过一次服务器被当跳板扫内网,你就能理解为什么我在每次代码评审时都要追着问“这个 url 是从哪来的”。安全不是靠某一个神级方案,而是靠每一个细节都不给攻击者留可乘之机。希望这篇内容,能让你在遇到类似逻辑时多留一个心眼。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 23:56:32

SQL注入原理与防御实战:从数据库安全到参数化查询的完整指南

1. SQL注入到底是什么:从一道“不该通过”的登录说起聊SQL注入之前,先讲一个我早年做项目时遇到的真实案例。某公司内部系统上线没多久,运维反馈后台登录接口响应偶尔很慢,偶尔又很快,而且数据库负载波动明显。一开始怀…

作者头像 李华
网站建设 2026/10/22 23:49:47

ELM极限学习机原理及MATLAB回归预测实现详解

做回归预测,我最早接触的是BP神经网络。那会儿为了一个隐含层节点数反复试,梯度下降又慢,动不动就陷进局部最优,调参调到怀疑人生。后来在一次实际任务里遇到极限学习机,也就是ELM,我第一反应是&#xff1a…

作者头像 李华
网站建设 2026/10/22 23:44:24

Spring Boot测试实战:JUnit 5集成与测试切片技术详解

Spring测试这块,我一直觉得是被低估的宝藏。标题里两个关键词——JUnit 5集成与测试切片技术——前者解决“测试怎么写、怎么跑”的问题,后者解决“测试跑得慢、依赖重”的问题。很多团队项目跑一遍全量测试要十几分钟,一大半时间都耗在等测试…

作者头像 李华
网站建设 2026/10/22 23:43:08

.NET桌面应用自动更新方案详解:ClickOnce、Velopack、MSIX与自研选型指南

做前端 Web 的同事可能很难理解,为什么桌面端一个“检查更新”的按钮做起来能折磨人这么久。但凡是发过几个 .NET 桌面应用的人,基本都体会过这种状态:修完 bug 兴冲冲打好包传给用户,第二天用户还是用着老版本,然后拿…

作者头像 李华
网站建设 2026/10/22 23:43:07

TKJ电梯轿厢结构设计全流程:从参数计算到SolidWorks建模与CAD出图

1. 先理解TKJ系列电梯和轿厢设计的本质干过电梯结构设计的朋友应该都有同感:TKJ系列轿厢结构设计是个很经典的老课题。不管是高校机械专业的课程设计、毕业设计,还是制造企业里新机型的结构开发,基本都绕不开这套流程——用SolidWorks做三维建…

作者头像 李华
网站建设 2026/10/22 23:34:44

Java+Android智慧校园双端源码:从环境搭建到二次开发全流程拆解

简介:这是一套面向高校计算机相关专业学生与教师的智慧校园Android客户端及后台管理系统完整项目源码,定位为高分毕业设计参考方案,也可用于课程设计、作业或项目初期立项演示。项目围绕校园资讯浏览、活动比赛公告、社团与班级团队管理展开&…

作者头像 李华