背景最近整理内部靶场和真实告警时,发现 SSRF 仍然是云上业务里比较容易被低估的一类风险。它不像 SQL 注入那样有明显的数据回显,也不像文件上传那样容易看到落地文件,但一旦应用具备访问内网或云厂商元数据服务的能力,影响面会迅速扩大。这篇文章不讨论攻击利用细节,主要从防守视角复盘一个典型场景:业务系统提供 URL 抓取能力,输入校验不足,导致服务端可以向非预期地址发起请求。重点放在识别方法、代码审计点、验证方式和治理策略上,适合做日常安全评审和开发自查参考。典型场景很多业务都会实现类似能力:用户提交头像 URL,服务端下载并转存。文章发布时抓取远程封面图。Webhook 或回调地址测试。在线截图、PDF 生成、链接预览。导入第三方资源时,后端主动请求用户提供的地址。这些功能的共同点是:用户可控输入最终进入了服务端网络请求函数。只要边界处理不严格,就可能让服务端访问本不应该访问的地址。SSRF 的核心不是“能不能访问某个特殊地址”,而是“用户是否能够影响服务端请求目标”。只要这一点成立,就应该进入风险评估。问题表现在一次排查中,某个链接预览接口接受参数 url,后端会拉取目标页面标题和描述。最初开发只做了协议判断,禁止了 file://,但允许 http 和 https。问题在于:没有限制目标域名范围。没有识别内网 IP、回环地址、链路本地地址。没有处理 DNS 解析后的地址校验。允许自动跟随重定向。请求超时时间较长,容易被用于探测内部服务存活。从日志看,请求来源都是应用服务器自身,传统 WAF 很难直接判断是否异常。如果只看入口参数,也可能误以为是普通 URL 预览行为。技术分析SSRF 审计时建议围绕“输入、解析、校验、请求、重定向、响应处理”六个环节展开。1. 输入是否可控常见危险参数名包括 url、uri、link、target、callback、webhook、image、avatar、fileUrl、redirect 等。不是说出现这些参数就有漏洞,但需要确认它们是否进入了网络请求逻辑。grep -R \"http.Get\\|HttpClient\\|RestTemplate\\|requests.get\\|curl_exec\\|URLConnection\" ./src代码审计中,比参数名更可靠的是查调用链。例如从 Controller 或 Handler 入参开始,追踪到 HTTP 客户端调用处。2. URL 解析是否一致很多 SSRF 绕过都来自解析差异。应用层校验使用一种解析器,实际请求库使用另一种解析器,二者对特殊格式的处理可能不同。治理时要避免自己手写字符串判断,例如只判断是否包含 127.0.0.1 或 localhost。推荐做法是:使用语言标准库解析 URL,规范化后再校验协议、主机名、端口,并在 DNS 解析后校验最终 IP。3. DNS 解析后的地址是否安全只校验域名本身是不够的。攻击者可以使用一个看似正常的域名,解析到内网地址或回环地址。更复杂的情况是 DNS Rebinding,第一次解析为公网 IP,通过校验后,实际请求时变成内网 IP。防护思路是将解析和请求绑定起来:应用先解析目标主机,校验解析结果不属于禁用网段,再使用经过校验的地址发起请求,并限制 Host 头和 TLS 校验策略不要被随意篡改。实际工程里也可以在网络层做 egress 策略,禁止应用容器访问元数据地址和内部敏感网段。常见需要拦截的地址范围类型示例说明回环地址127.0.0.0/8、::1访问本机服务私有地址10.0.0.0/8、172.16.0.0/12、192.168.0.0/16访问内网服务链路本地地址169.254.0.0/16云元数据服务常见网段本地链路 IPv6fe80::/10IPv6 场景不能忽略未指定地址0.0.0.0、::不应作为目标访问4. 重定向是否重新校验很多实现会先校验用户传入的 URL,然后 HTTP 客户端自动跟随 302 跳转。如果跳转后的 Location 指向内网地址,而代码没有再次校验,就会绕过第一层防护。建议关闭自动重定向,手动处理 3xx 响应。每一次跳转都重新执行完整校验,并设置最大跳转次数,例如不超过 3 次。# 防守验证时可观察是否存在异常跳转链,不建议在生产环境直接压测
curl -I --max-redirs 0 响应内容是否被原样返回如果接口把服务端请求到的内容直接返回给用户,风险会更高。即使不能访问敏感服务,也可能造成内部接口信息泄露。建议只返回业务需要的最小字段,例如图片尺寸、标题摘要、状态码,不要透传完整响应体、响应头和错误堆栈。关键步骤:如何做一次安全自查步骤一:梳理服务端出网点先从代码和配置层面列出所有会主动发起 HTTP、gRPC、TCP 请求的位置。重点关注:URL 预览、截图、PDF 生成。文件下载、图片转存。Webhook 配置和测试。第三方导入、开放平台回调。代理转发、诊断工具类接口。如果服务使用了统一 HTTP SDK,可以在 SDK 层增加日志,记录目标协议、域名、解析 IP、端口、调用方方法名,方便定位风险入口。步骤二:建立允许列表优先使用允许列表,而不是黑名单。比如某个功能只允许抓取公司 CDN 的图片,就只允许指定域名后缀或固定资源域名。不要让一个头像上传功能具备访问任意 URL 的能力。allowed_hosts:
- static.example.com
- img.example-cdn.com
blocked_cidrs:
- 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/128
- fe80::/10对于确实需要访问外部任意站点的功能,也应该至少限制协议、端口、请求方法、超时时间、响应大小和跳转次数。步骤三:限制协议和端口通常只允许 http 和 https。端口建议限制在 80、443 或业务明确需要的端口。不要允许 gopher、dict、ftp、file 等协议进入请求链路。需要注意的是,有些 HTTP 客户端或 URL 解析器会接受大小写混合、前后空白、编码字符等形式。校验前应做规范化处理。步骤四:设置超时和响应大小SSRF 不只会带来数据泄露,也可能引发资源消耗。建议设置较短的连接超时和读取超时,并限制最大响应体大小。// 示例:伪代码,仅表达防守侧配置思路
client.setConnectTimeout(2000);
client.setReadTimeout(3000);
client.setMaxResponseSize(1024 * 1024);
client.setFollowRedirects(false);步骤五:云环境中屏蔽元数据访问云上环境要特别关注实例元数据服务。不同云厂商实现不同,但链路本地地址是重点风险区域。治理上不要只依赖应用代码,建议叠加网络层策略:容器或主机层禁止普通业务进程访问元数据地址。使用云厂商提供的元数据访问防护能力,例如强制使用带令牌的版本。最小化实例角色权限,避免一个业务服务拥有过大的云资源权限。对访问元数据地址的行为建立审计告警。这里的重点是纵深防御:即使某个业务接口存在 SSRF 缺陷,也不应该让它轻易触达云凭据。日志与检测建议SSRF 检测不能只看入口 HTTP 日志,还要看服务端出网日志。建议记录以下字段:业务接口名和调用链路 ID。用户提交的原始 URL。规范化后的 URL。解析得到的 IP 列表。最终连接的 IP 和端口。是否发生重定向以及跳转目标。响应状态码、响应大小、耗时。告警规则可以从这些方向入手:目标 IP 命中内网、回环、链路本地网段。同一用户短时间访问大量不同端口或不同内网地址。URL 预览接口出现异常高失败率或超时率。出现指向元数据地址的请求。重定向目标从公网域名跳转到非公网地址。代码层防护示例下面是一个偏伪代码的校验流程,用来表达工程上的处理顺序。不同语言实现会有差异,但核心思路类似。function validateOutboundUrl(input):
url = parseAndNormalize(input)
if url.scheme not in [\"http\", \"https\"]:
reject(\"unsupported scheme\")
if url.username or url.password:
reject(\"userinfo not allowed\")
if url.port not in [80, 443, null]:
reject(\"port not allowed\")
if not hostInAllowlist(url.host):
reject(\"host not allowed\")
ips = resolveDns(url.host)
if ips is empty:
reject(\"dns resolve failed\")
for ip in ips:
if isPrivate(ip) or isLoopback(ip) or isLinkLocal(ip) or isReserved(ip):
reject(\"unsafe address\")
return url实际请求时,如果遇到重定向,需要对新的 Location 重新执行 validateOutboundUrl。不要只在第一次请求前校验。容易忽略的细节IPv6:很多系统只拦截 IPv4 私有网段,忽略了 ::1、fe80::/10 等地址。短地址和非标准写法:不同语言对数字 IP、八进制、十六进制格式支持不同,建议统一使用成熟 IP 库解析。DNS 缓存:校验时解析和请求时解析不一致会带来竞态,必要时在网络层兜底。错误信息:不要把连接失败原因、内部地址、端口探测结果完整返回给用户。代理配置:应用如果配置了 HTTP_PROXY、HTTPS_PROXY,要确认不会把请求转发到非预期网络路径。第三方组件:截图服务、PDF 渲染服务、爬虫服务往往是独立进程,也要纳入同样的出网控制。事件处置思路如果已经发现疑似 SSRF 行为,建议按以下顺序处理:先在网关或应用层临时限制高风险接口的外部 URL 输入。排查过去一段时间的服务端出网日志,重点看内网地址、链路本地地址和异常端口。检查云平台审计日志,确认是否存在异常凭据访问、角色调用或资源枚举。轮换可能暴露的临时凭据或密钥,收敛实例角色权限。修复代码校验逻辑,并补充单元测试和安全回归用例。在容器网络、主机防火墙或云安全组层面增加出网限制。总结SSRF 的治理不能只靠一个正则或一段黑名单。比较稳妥的方式是:业务上尽量使用允许列表,代码上做规范化解析和 DNS 后校验,请求过程中限制重定向、端口、超时和响应大小,基础设施层面限制业务服务访问敏感网段和云元数据服务。从长期维护角度看,最有价值的是把“服务端出网”当成一类基础安全能力来管理,而不是每个业务各写一套 URL 校验。统一 SDK、统一日志、统一网络策略,能显著降低遗漏概率,也方便后续审计和应急。
推荐意见