SSRF攻击这名字听起来挺唬人,但实际上在漏洞圈里它一直是个常青树。跟SQL注入、XSS这种直接打前台的漏洞不一样,SSRF攻击利用的是服务端主动发起的请求,攻击者通过伪造这个请求,把目标指向内网、云元数据、甚至其他敏感接口。说白了,就是让服务器帮你去摸内网的家底,然后把结果通过各种渠道带出来。这篇文章我就结合自己搞安全测试和代码审计的实践经验,从攻击面分析、触发场景、实操复现、绕过手法到最终防护,完整拆解一遍SSRF攻击。
1. SSRF攻击的核心概念与攻击面分析
1.1 什么是SSRF攻击,为什么它是高危漏洞
Server-Side Request Forgery,服务端请求伪造,这类漏洞的本质是“服务端对用户提供的URL或网络请求参数缺少充分校验”,导致攻击者能控制服务端去访问任意地址。你可能觉得“这不就是让服务器帮忙访问个网页吗,能有多大危害”,我带你看几个真实的攻击目标,你就明白为什么圈内普遍把它列为高危甚至严重级别的漏洞。
先说说云环境下的攻击路径,这是SSRF攻击如今最热门的利用方向。很多应用部署在云主机上,应用本身拥有访问云元数据服务的权限。元数据服务通常是一个特殊的内部地址,比如某云厂商的元数据接口,它保存着这台云主机的临时访问凭证、角色信息、初始化配置等敏感数据。攻击者只要能通过SSRF请求到这个内部地址,就能把云主机的身份凭证偷出来,然后直接用这个凭证去操作云上的其他资源。换句话说,你打进的不只是一个应用,而是整台云主机的身份权限。我接触过的不少云上企业被拖库、被加密勒索,溯源下来入口就是一处不起眼的SSRF。
再说内网横向渗透路径。现代企业的内网环境里布满了各种管理系统、数据库、Redis实例、消息队列、监控面板。这些系统很多只绑定了内网IP,默认不接受外网请求。外部攻击者打不到这些系统,但应用服务器作为“跳板”就完全不同了,它同时处于内网和外网的交叉位置。SSRF攻击让攻击者借用这个跳板向内网发起扫描和探测,一旦发现某个内网Redis服务配置薄弱,直接通过Gopher协议发起指令,拿到写定时任务或SSH公钥的能力,整台内网主机就沦陷了,接下来就是横向漫游,连锁反应。
换句话说,SSRF攻击解决的是“攻击者无法直接从外部触达内网目标”这个问题。它把攻击路径从“外网-应用”延伸成“外网-应用-内网-任意主机”,攻击半径瞬间扩大好几倍。
1.2 哪些行业和系统更容易踩坑
聊完危害,说说实战中哪些环节最容易出现这类漏洞。我做过大量代码审计,SSRF攻击的高发场景其实非常集中,主要有这么几类:
- 图片处理和头像上传功能:用户提交一个图片URL,服务器去抓取并处理,这是最常见的SSRF入口。
- PDF生成服务:用户传一个URL,服务端渲染成PDF,比如发票下载、报表导出,也是一个经典入口。
- Webhook配置功能:用户在后台填一个回调地址,服务端向这个地址推送事件通知。
- 代理转发和API网关:有些应用自己实现了转发逻辑,比如“在线代理”、“页面快照”、“URL翻译”,本质是服务器替用户访问目标网页。
- 文档导入和爬虫功能:导入XML、SVG、PDF时解析外部实体或附件URL,或爬虫功能允许传入站点地址。
- 监控系统和告警平台:配置数据源地址、添加监控目标URL等场景。
- 微服务架构中的服务间调用:A服务根据用户请求拼装URL去请求B服务,参数如果可控,同样会出问题。
从行业来看,内容型产品、电商后台、企业管理类SaaS是重灾区,因为这些产品天然就有“抓取外部资源”的需求。还有一个容易被忽视的点:现在很多大厂把业务拆成微服务,内部开放了数百个HTTP接口,这些接口内部调用时往往默认互相信任,不做鉴权。外部用户只要能通过某个边缘服务发起SSRF请求到达这些内部接口,就相当于拿到了一把打开内部系统的万能钥匙,参与过微服务改造的开发者应该深有体会。
2. SSRF攻击的常见触发场景与靶点分析
2.1 五大高发功能场景的代码视角拆解
从代码层面看,SSRF攻击的根因往往是开发者在发起HTTP请求时直接使用了用户可控的URL参数。挑五个典型场景细说。
图片远程抓取:很多社区和内容平台为了让用户省去本地上传的麻烦,支持“粘贴图片链接”来远程加载图片。后端代码通常是这样的逻辑:接收用户提交的URL,用HTTP客户端库发起GET请求,把返回的字节流保存到本地。问题出在几乎所有的HTTP客户端库都支持请求任意协议和任意地址,默认并没有限制只能访问外网或只允许HTTP协议。攻击者把URL改成一个内网地址,比如本机的管理端口或云元数据地址,服务器就会乖乖替你去访问。
PDF导出服务:用户提交一个URL,服务端启动无头浏览器去访问这个URL,然后把渲染结果导出成PDF。这是SSRF攻击的重灾区,因为渲染引擎不仅能发起HTTP请求,还能执行JavaScript、加载外部资源,花样更多。攻击者可以构造一个HTML页面,里面嵌入内网IP的图片或iframe,渲染时就会自动请求内网资源;更极端的是利用无头浏览器去访问内网系统的后台,把后台页面直接导出成PDF拿回来慢慢看,敏感信息直接大摇大摆出网。
Webhook回调:用户在控制台配置一个“订阅事件接收地址”,比如订单创建后通知到某个URL。开发实现时一般就是接收到事件就对用户配置的URL发一个POST请求。这里的URL完全由用户配置,如果产品允许用户修改回调地址到任意URL,攻击者就可以把回调指向内网接口,甚至指向自己公网服务器以观察请求内容。更危险的是Webhook往往带上了签名或Token,攻击者拿到这个请求就可能伪造合法事件。
文件预览/代理下载:一些办公套件或云盘产品支持“在线预览外部文档”、“在线读取远端文件”。实现上就是服务端代理用户去请求远端文件再回显给用户。这类功能本质上是把服务端变成了一个代理,如果对协议和IP限制不严,攻击者可以直接访问内网Web管理面板,然后在内网Web业务里做二次利用,比如拼接路径读取敏感文件,整个内网直接暴露在代理之下。
URL导入/链接抓取:用户提交一个站点地址,服务端去抓取页面的标题、描述、缩略图,用于分享卡片预览。这类功能同样会把服务端变成请求代理。分享链接功能大家每天都能遇到,因此也是SSRF攻击出现频率极高的场景。
2.2 攻击目标靶点:从端口扫描到云凭证窃取
明确了触发场景之后,再看看攻击者拿到SSRF后通常会瞄准哪些内部资源。
内网资产探测是第一步。攻击者会用SSRF对目标内网IP段发起批量访问,通常使用IP和端口号构造URL,通过响应时间、端口状态、页面内容差异来判断内网存活主机和开放端口。这里的“内网IP段”通常指RFC1918定义的私有地址,比如常见的10.x.x.x、172.16.x.x到172.31.x.x、192.168.x.x。很多时候不需要扫全段,优先扫网关、DHCP、DNS、管理网段等常用地址,命中率非常高。
端口扫描也是SSRF的重要用途。因为服务端可以访问任意IP和端口,攻击者相当于在内网中拥有一个扫描器,可以针对某个目标IP批量探测22、3306、6379、9200、8080等常见服务端口。比起传统外网端口扫描,这种扫描更隐蔽,因为流量来源是内网应用服务器,目标系统的告警设备如果只关注外网来源,就会漏掉这一类痕迹。
云元数据获取是云场景下的核心目标。云平台为方便实例管理和配置下发,都会提供一个特殊的元数据服务地址。主流云厂商都统一到该地址,攻击者只要让SSRF访问到它,就能拿到实例ID、主机名、甚至临时安全凭证。拿到临时凭证后还可能进一步枚举存储桶、镜像、快照等资源,破坏面非常大。
本地服务访问属于比较容易被忽略但实际效果好的目标。应用服务器上往往同时运行着一些本机管理服务,比如Docker API、数据库管理工具、调试用端口等。SSRF请求localhost或127.0.0.1,就可能直接触达这些本机服务。例如某些数据库管理工具绑定了本机端口且免密,SSRF攻击结合特定协议就能直接执行命令。
最后是内网其他系统的高危接口利用。内网系统之间的信任关系往往比外网宽松,很多内网接口没有鉴权或鉴权很弱。攻击者用SSRF扫描出内网环境后,可以直接向内网系统的管理接口发送精心构造的请求,完成增删改查操作,进一步渗透内网。到这里你应该明白,SSRF攻击的真正价值在于“让攻击者获得了内网侦察和横向移动的跳板”。
3. SSRF攻击的实操复现与命令行详解
3.1 搭建一个本地实验环境
研究了这么多,上点实操的。要深入研究SSRF攻击,建议自己搭一个实验环境,我平时用的是完全虚拟化的方式,不碰任何真实目标系统,这一点非常关键。
推荐组合是虚拟化平台加两台虚拟机,一台作为目标应用服务器,装一个存在SSRF漏洞的Demo应用,另一台作为内网靶机,跑一个简单的Redis服务或者Web管理面板模拟内网资产。网络层面把目标应用服务器配成双网卡,一块桥接到物理网络,一块连接到仅主机模式的虚拟内网,这样就能模拟“外网可达但内网隔离”的真实拓扑。
Demo应用不用太复杂,一个支持远程图片URL拉取功能的PHP应用就够用了。核心代码大概长这样:
<?php $url = $_GET['url']; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); $data = curl_exec($ch); if ($data !== false) { header('Content-Type: image/png'); echo $data; } else { echo 'fetch failed'; }这段代码接收一个url参数,直接用cURL去请求它,请求结果原样返回。没有对协议、IP、端口、重定向做任何限制,是教科书级的SSRF漏洞写法。我在自己验证时通常还会加一句error_reporting(E_ALL)把错误回显打开,这样内网端口探测时更容易通过响应差异做判断。
3.2 一步步模拟内网探测与数据窃取
环境就绪后,按如下步骤模拟攻击。第一步是确认SSRF存在并且能访问内网。先在应用服务器上确认本机IP和内网网段,例如内网网段是172.16.10.0/24,目标应用服务器IP是172.16.10.10,模拟的内网Redis靶机是172.16.10.20。攻击者站在外网,实际无法直接访问172.16.10.20,但通过SSRF可以。
先用curl直接访问内网IP的任意端口,验证连通性:
curl -v "http://你的应用地址/fetch.php?url=http://172.16.10.20:8080/"如果响应显示连接超时或拒绝,说明内网不可直达,符合前提。随后把这个URL作为参数传给漏洞应用:
curl -v "http://你的应用地址/fetch.php?url=http://172.16.10.20:8080/"观察响应,如果Demo应用返回了这台内网主机的页面内容,说明SSRF已经打通了内网隧道。此时等于是拿到了一个内网代理,接下来就是端口探测:
curl -s "http://你的应用地址/fetch.php?url=http://172.16.10.20:6379/"端口6379没有正常HTTP响应,但应用可能返回连接成功后的错误信息,或者因为响应差异表现为超时/立即失败。实际测试时用一个简单的循环脚本扫描端口会更高效:
for port in 22 3306 6379 9200 8080; do code=$(curl -s -o /dev/null -w "%{http_code}" "http://你的应用地址/fetch.php?url=http://172.16.10.20:$port/") echo "port $port => $code" done注意这里的判断不只看HTTP状态码,因为很多非HTTP服务不会返回标准状态码。完整利用时通常是观察响应时间、返回长度、错误特征三者配合。
确认6379端口开放后,尝试读取内网Redis信息。如果Redis版本较老且未设密码,可以在URL的路径里写入命令:
curl -g "http://你的应用地址/fetch.php?url=dict://172.16.10.20:6379/info"dict协议是cURL默认支持的协议之一,Demo应用没有限制协议,所以可以借此向内网Redis发送info命令,直接看到Redis的运行版本、内存相关信息、持久化配置等。若是进一步利用,还可以通过Gopher协议构造复杂指令。这里仅做演示,作为安全测试人员,测试到这里已经足够说明SSRF的危害了,无需真的去执行敏感操作。
3.3 Redis未授权访问与Gopher协议扩展利用
Redis未授权访问是SSRF攻击Gopher协议最经典的组合利用方式。很多内网Redis因部署在可信网络,管理员可能忽略了密码设置。攻击者一旦通过SSRF确认Redis6379端口可达且无需认证,就可以尝试通过Gopher协议发送Redis命令,达到写文件、反弹Shell甚至主从复制加载恶意模块的效果。
Gopher协议本身是一种古老的互联网协议,格式是gopher://host:port/_后跟原始TCP数据,数据中空格用%20代替,换行用%0d%0a代替。比如向Redis发送一条写SSH公钥的命令,利用方式大致是先把需要写入的内容和命令拼成Redis协议格式,再做URL编码。这里有一个非常关键的细节:很多人在用Gopher构造Redis数据时容易在payload末尾忘记追加CRLF换行,导致命令不完整,Redis不执行。我在调试时踩过这个坑,后来养成了每个payload都以%0d%0a结尾的习惯。
构造完毕后再用curl去触发:
curl -g "http://你的应用地址/fetch.php?url=gopher://172.16.10.20:6379/_*3%0d%0a..."成功的话,内网Redis就会执行对应的写文件或配置命令。在实际攻防场景里,这通常意味着可以直接往目标服务器的/root/.ssh/authorized_keys里写入公钥,或者往Web目录写入一句话脚本,直接拿下内网主机权限。到这里你应该完全理解为什么SSRF攻击常常被评为严重级别,因为它可以把一个“只能读读内网页面”的漏洞升级成“内网主机任意命令执行”。
4. SSRF攻击的检测方法与绕过手法剖析
4.1 黑盒检测思路与白盒代码审计要点
搞安全测试这么多年,SSRF攻击的检测我基本分为黑盒和白盒两条路。黑盒场景下没有源代码,只能通过功能行为和响应差异去判断。
黑盒检测首先要找到“发起服务端请求”的功能点,这个靠功能理解。有URL输入框、有上传远程图片、有导入外部链接、有回调地址配置的地方都值得重点关注。找到点之后,用自己可控的服务器去观察回连。这个方法非常可靠:在公网准备一台带监听功能的VPS,把URL参数改成VPS地址,观察VPS能否收到请求以及收到什么样的请求头、请求参数。如果收到了来自目标应用服务器的请求,说明SSRF存在,而且能把请求头里的内部IP信息一并带出来,为后续利用提供线索。
另一种黑盒思路是用时间盲注的方式判断。目标地址不同,服务端访问的响应时间会明显不同。例如访问一个不存在的公网IP时的连接超时时间,与访问一个内网存活IP时的瞬间连接时间存在差异。通过反复调整URL和观察时间,能判断是否真的发起了服务端请求。这种方式不需要外部VPS,适合快速批量验证。
白盒检测则要重点审查所有发出HTTP请求的代码位置。我在代码审计时一般会先全局搜索常用的HTTP库名称,比如Python的requests、urllib,Java的HttpClient、URLConnection,PHP的cURL,Node.js的axios、http等。凡是这些库的调用点,往上追溯URL参数来源,如果URL可以由用户输入或从用户可控的数据中拼接而来,就要进一步确认有没有针对协议、IP、端口的限制。
白盒审计中特别容易遗漏的是间接可控参数。比如用户上传一个XML文件,XML里引用了外部实体,服务端解析这个XML时去请求了外部URL,这类XXE也算一种间接SSRF。又比如应用从请求头中取X-Forwarded-For或Host来拼URL,这些请求头用户可以直接修改,一样是可控点。还有JSONP回调、跳转参数这些“半可控”来源,也曾经让我在审计时下过结论,后来发现攻击者仍然可以通过特殊字符注入改变路径,防不胜防。
4.2 常见绕过手法与防御边界突破
检测和利用SSRF攻击,很多时候还要研究绕过后端防护。开发者如果意识到SSRF漏洞存在,通常会加一些简单的过滤,常见的是“黑名单关键字”和“内网IP检测”。两种都能绕,套路也很成熟。
黑名单过滤,比如过滤http、https、localhost、127.0.0.1、10.0.0.0等关键字。这类防护的第一招是用不同协议绕过,当HTTP被过滤时,尝试dict、gopher、file等协议,很多防护只拦了HTTP,没有考虑Gopher这类扩展协议。第二招是URL解析差异绕过,后端如果用正则简单匹配明文URL,攻击者可以变换写法,比如大小写混合、url编码、使用重定向跳转、利用IPv6地址格式、十进制或八进制表示的IP地址。还有一种经典写法是http://0x7f000001/,这个十六进制形式等价于127.0.0.1,黑名单如果只匹配localhost和127.0.0.1就会漏掉。
IP白名单检测,只允许访问特定公网域名或IP段。这种防护看起来更稳,但也有软肋。比如有些实现先用DNS解析域名拿到IP做校验,通过校验后再用原来的域名去发起请求,中间存在TOCTOU时间竞争,DNS第一次解析返回一个白名单IP,但第二次解析返回了内网IP,攻击者通过自建DNS服务做轮换,就能绕过白名单。又比如校验逻辑只检查了URL中的host部分,而URL本身带了重定向,一个合法白名单URL在服务端跟随重定向后跳到了内网地址,防护形同虚设。
再补一个非常实用的绕过知识点:URL解析差异。很多语言内置的URL解析函数与HTTP客户端实际的请求目标并不完全一致。攻击者利用这种差异构造特殊URL,例如在地址中嵌入@符号、添加用户名密码信息、使用反斜杠、用问号和井号截断,让“看起来的host”和“实际请求的host”不一致,从而骗过校验。这种手法在基于字符串匹配的防护面前几乎一用一个准,核心原因是“语义层面的解析差异很难通过简单的字符串检查消除”。
5. SSRF攻击的修复方案与防御体系建设
5.1 代码层面的修复要点与白名单方案
聊完攻击手段,到防守方视角。代码层面修复SSRF攻击,核心思路就一句话:不要相信用户提供的URL,在发起请求前做严格的协议、地址、端口校验。
首先是协议白名单。绝大多数业务场景只需要HTTP和HTTPS,那就把协议严格限定为这两种,其他协议如file、dict、gopher、ftp、tftp一律拒绝。实现方法是解析URL并检查scheme字段,而不是去匹配字符串。这一步能防住协议扩展这一大类攻击。
其次是IP地址校验。拿到URL后,先把域名解析成IP,确认解析结果不是内网IP、保留IP、链路本地地址。这一步不能只看host字符串是不是localhost、127.0.0.1,因为域名可以解析成任意地址,比如攻击者用一个域名解析到内网IP,字符串上完全合法,实际请求却进了内网。正确的顺序是:先解析域名,拿到所有解析结果,逐一检查是否落在内网地址段范围内,全部合法才放行。
再强调一个很多人忽略的细节:防止重定向绕过。即使第一步校验通过了,请求发起后如果服务端自动跟随重定向,攻击者可以先用一个合法公网URL通过校验,服务端跟随重定向时跳到内网地址。修复方案是关闭自动重定向,或者对每次重定向都重新执行地址校验。如果业务确实需要跟随重定向,必须在重定向循环里递归校验,这个逻辑不写的话,前面做的所有校验都可以被一条302绕过。
补充一个实用配置:最后访问之前再做一次目标地址确认。即请求发起前再次解析当前将要连接的host,确认无误后才建立连接,这样能有效缓解DNS重绑定攻击。
5.2 网络层纵深防御与常见陷阱规避
代码层修复之外,网络层面的纵深防御同样重要。单靠应用层代码做白名单,始终存在被0day绕过或逻辑漏洞击穿的风险。把内网元数据服务和敏感接口与应用服务器隔离,是最直接的兜底手段。
云平台方面,主流的云厂商现在基本都推出了元数据服务访问限制能力,可以配置成只有特定角色或特定网络命名空间的实例才能访问元数据地址,或者在VPC层面配置网络ACL,禁止非必要实例访问元数据网段的地址。我在测试环境验证过,开启限制后SSRF即使能访问到这个IP,也会因为网络策略被拒绝,攻击面直接封死。
网络ACL和防火墙是另一道屏障。建议将云主机的出方向访问策略改为白名单模式,只允许访问公网业务端口和必要的外部服务端口,禁止出方向访问非业务必需的内网网段。当然这一步需要和业务方充分沟通,出方向限制如果配得太严,会影响正常业务,所以通常的做法是先让业务方梳理出完整的依赖清单,再基于清单去配置策略。
还有一个反复出现的陷阱:错误信息泄露。很多应用在请求失败时会原样输出cURL的错误信息,比如“Connection refused”、“Connection timed out”、“DNS resolution failed”,这些信息对攻击者来说是极好的探测反馈,可以帮助他们判断内网IP和端口状态。真正安全的做法是在错误提示里统一返回“远程资源获取失败”,把细节全部打到服务端日志里。关于日志,需要明确记录发起请求的原始URL、目标IP、协议以及最终跳转链,方便事后追溯。
5.3 自动化检测与上线前体检
最后说说怎么把SSRF攻击检测自动化,纳入上线前的安全体检流程。对安全建设成熟度中等的团队,最实用的方式是编写一个轻量级扫描脚本,核心能力是“可控回连探测”。
我在实际执行时通常是这样的:准备一个公网可访问的监听服务,把需要测试的URL注入点批量跑一遍,每个输入点用一系列测试Payload替换URL参数,包括公网回连地址、内网地址、file协议、DNS外带域名等。如果监听服务收到了来自目标服务器的请求,就可以判定为SSRF漏洞,并把请求中的目标IP、路径、User-Agent记录到报告里。
在自动化脚本的实现上,DNS外带是特别有效的一招。即使目标服务器没有回显,也没有公网VPS监听服务,只要它能发起DNS解析请求,就可以把随机子域名拼接成内网IP加端口的形式,例如test.内网IP.port.6379.your-dns-log-server.com,如果DNS日志里出现了这条解析记录,说明服务器确实发起了针对内网IP的请求,漏洞确认成立。
还有一个在自动化检测中很实用的维度:请求延迟差异。在内网端口探测时,开放端口、关闭端口、被防火墙丢弃的响应时间往往有明显差异,比如开放端口瞬间响应,防火墙丢弃则等到超时。自动化工具可以采样多次,取平均响应时间,用统计学方法识别异常端口,降低误报率。这个思路不复杂,但非常有效。
上线前的体检不只跑扫描器,还要结合代码审计的结果一起看。推荐的做法是:在CI流水线接入SAST工具,对代码中的HTTP请求调用做污点追踪分析,把用户可控参数到HTTP请求URL的路径直接标记出来。虽然SAST工具存在误报,但可以结合人工审计做分类验证,把真正的高危路径筛出来,再结合DAST扫描结果交叉确认。如此下来,大部分SSRF隐患都能在上线前暴露。
写在最后的一些实战心得
SSRF攻击做了这些年,我最大的感受是“防守方总在跟URL解析较劲”。这个漏洞不像SQL注入那样有统一的语法结构,它依赖的是应用本身的功能逻辑,以及各个编程语言、HTTP库对URL五花八门的解析方式。你写白名单时以为覆盖了所有情况,结果攻击者用一个URL编码的换行符、一个IPv6地址、一段302跳转就绕过去了。所以真正稳妥的方案从来不是某个单一函数能把控的,而是应用层校验加网络层隔离加云平台限制的多层防御。
另外想提醒刚入行的朋友,练手务必在授权环境下进行。自己搭虚拟机做实验是一回事,拿别人的站点做测试又是另一回事。安全测试的前提永远是授权,这一点想清楚了,后续的职业道路才能走得稳。我个人在实际测试中最常用的一套组合拳就是“公网回连加DNS外带加内网字典探测”,它能让SSRF漏洞在五分钟内暴露原形。希望这篇文章能帮你在理解SSRF攻击、复现漏洞和构建防御的路上少踩几个坑。