服务端请求伪造(SSRF)并不是一个只靠拦截几个关键词就能解决的问题。它通常出现在图片抓取、Webhook、文档预览、远程资源导入、健康检查等功能中,真正的风险取决于请求发起位置、目标地址解析方式、网络出口权限以及返回结果是否可被攻击者观察。在实际审计中,单纯搜索 curl、HttpClient 或 URL.openConnection 往往会产生大量噪声。更有效的方法是从数据流入手,确认用户可控数据是否最终进入了网络请求组件,再结合网络边界和业务权限判断影响范围。本文以代码审计、实验验证和事件复盘为主线,整理一套适合长期使用的 SSRF 分析方法。一、先明确 SSRF 的风险边界SSRF 的核心问题不是服务端发起了请求,而是外部输入影响了服务端请求的目标或请求内容。根据可见性和网络能力,大致可以分为以下几类:有回显 SSRF:服务端将响应状态、响应头或响应正文返回给调用者,通常更容易确认目标是否可达。盲 SSRF:调用者看不到响应内容,只能通过状态变化、延迟、日志或带外监测确认请求是否发出。受限 SSRF:只能访问固定协议、固定域名或固定端口,但仍可能造成内网探测或敏感接口调用。高权限网络 SSRF:应用部署在能够访问管理网、云平台控制面或内部服务的网络区域,风险通常显著高于普通业务主机。因此,漏洞评级不能只依据是否能访问某个内网地址,而应同时考虑网络位置、服务账号权限、响应可见性、请求方法、是否携带认证信息以及是否能影响内部业务状态。二、代码审计:从输入点追踪到请求点审计时建议先建立两个清单:一类是可能接收 URL 或主机名的输入点,另一类是实际发起网络访问的危险汇点。前者包括 JSON 字段、表单参数、消息队列内容、数据库中的同步任务以及管理后台配置;后者包括 HTTP 客户端、DNS 查询、文件下载器、图片处理器和浏览器自动化组件。典型的数据流如下:用户输入 url
-> 参数校验或业务对象封装
-> 重定向跟随 / DNS 解析
-> HTTP 客户端发起请求
-> 响应进入预览、解析或转存逻辑审计重点不应停留在入口处是否有正则,而要继续确认以下问题:校验发生在 DNS 解析之前还是之后。客户端是否自动跟随重定向,重定向后的地址是否重新校验。应用是否允许非 HTTP 协议,例如 file、gopher 或自定义协议。连接时使用的地址是否与校验时使用的地址一致。是否支持 IPv6、十进制 IPv4、IPv4 映射地址等不同表示形式。请求是否自动携带 Cookie、Authorization 或内部服务令牌。响应是否会被原样返回,或者被图片、XML、压缩包等解析器继续处理。三、常见的脆弱实现模式下面的代码仅用于说明审计思路。问题不在于使用 HTTP 客户端本身,而在于未经严格约束的用户输入直接控制了请求目标。public byte[] download(String target) throws IOException {
URL url = new URL(target);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(3000);
conn.setReadTimeout(5000);
return conn.getInputStream().readAllBytes();
}这类实现至少存在几个审计疑点:协议未限制、目标主机未限制、自动重定向行为未明确、响应大小未限制,并且返回内容可能被业务层当作可信文件继续处理。即使后续增加了字符串黑名单,也不能从根本上解决地址解析差异问题。另一个常见问题是只校验一次域名:URI uri = URI.create(input);
if (allowedHosts.contains(uri.getHost())) {
return httpClient.get(input);
}这里需要继续检查 HTTP 客户端的 DNS 解析、连接建立和重定向处理方式。校验阶段得到的主机名与连接阶段解析出的地址可能不是同一个时刻的结果,且重定向可以把请求带到另一个目标。对于重要功能,不能把安全边界建立在单次字符串比较上。四、推荐的分析流程1. 绘制请求链路先记录请求从入口到出口的完整链路,包括控制器、服务层、任务队列、网络客户端和响应处理模块。对于异步任务,还要确认任务创建者和实际执行者是否处于不同网络区域,避免只看同步接口而遗漏后台 worker。2. 识别实际网络权限检查容器网络、主机路由、代理配置、防火墙策略和服务间访问控制。应用代码看似只能访问公网,但如果运行环境默认允许访问管理网段,风险仍然存在。建议将网络权限视为漏洞影响的一部分,而不是部署完成后的附加问题。3. 在隔离环境中验证行为验证应使用自建的测试服务,不要直接探测生产内网。可以在本地实验环境启动一个仅监听回环或测试网卡的 HTTP 服务,记录请求方法、路径、Host、请求头和来源地址,再让待测程序访问该服务。验证目标是确认请求是否发出、哪些字段可控、是否跟随重定向以及响应如何流转。# 在隔离实验环境中启动本地测试服务
python3 -m http.server 18080 --bind 127.0.0.1
# 仅用于验证业务是否访问了指定测试地址
curl -i ' 检查解析和重定向差异在代码审查阶段重点关注 URL 解析库的行为。不同语言和库对用户名字段、端口、编码、IPv6、空白字符和异常 URL 的处理可能不同。修复时应使用成熟解析器完成规范化,并在规范化后的结构上做协议、主机、端口和地址类别判断。重定向是经常被忽略的边界。更稳妥的做法是默认关闭自动重定向;如果业务确实需要重定向,则每一跳都重新执行目标校验,并设置最大跳转次数、最大响应大小和总超时时间。五、防护实现的基本原则1. 优先采用允许列表如果业务只需要从少数固定站点获取资源,应直接保存站点标识或固定路径,不要让调用者提交任意 URL。允许列表最好绑定协议、主机、端口和路径前缀,不能只比较一个可变的域名字符串。2. 对解析后的地址做网络分类在建立连接前,对所有解析结果进行分类,拒绝回环地址、链路本地地址、私有地址、保留地址和未明确允许的内部网段。需要同时考虑 IPv4 与 IPv6,并处理域名解析返回多个地址的情况。连接建立后还应确认最终连接地址仍符合策略,避免解析和连接之间出现竞态。3. 限制协议、方法和资源规模只允许业务真正需要的协议,通常为 HTTPS 或受控的 HTTP。限制请求方法,下载类功能一般不需要允许任意方法。关闭或严格控制代理继承,避免环境变量改变请求出口。设置连接超时、读取超时、总耗时、响应大小和并发数。不要自动转发用户输入的认证头、Cookie 或内部追踪令牌。对下载内容执行类型检查,避免把远程响应直接交给高风险解析器。4. 将网络隔离作为第二道防线应用层校验并非绝对可靠。生产环境应通过出口代理、网络策略和防火墙限制应用只能访问必要的目标。不同服务使用不同的网络身份,避免一个普通图片服务同时拥有访问数据库管理面、消息系统和云控制面的权限。六、一个更安全的实现思路下面是伪代码,重点是流程而不是某个具体框架。实际项目中应使用经过审查的 URL 解析库和网络策略组件。URI target = parseAndNormalize(input);
if (!isAllowedScheme(target.scheme())) {
throw new ValidationException();
}
if (!isAllowedHost(target.host())) {
throw new ValidationException();
}
List<IPAddress> addresses = dnsResolve(target.host());
for (IPAddress address : addresses) {
if (isLoopback(address)
|| isLinkLocal(address)
|| isPrivate(address)
|| isReserved(address)) {
throw new ValidationException();
}
}
HttpClient client = buildClientWithoutAutoRedirect();
Response response = client.get(
target,
timeout = 5s,
maxBodySize = 2MB
);
if (!isAllowedRedirect(response)) {
throw new ValidationException();
}需要注意,单独封装一个 isPrivate 函数并不能自动保证安全。应明确其覆盖的地址类别、IPv6 行为、DNS 多地址行为以及连接时使用的地址。对于高风险业务,建议让统一网络访问组件负责策略执行,避免各个业务团队重复实现并产生不一致。七、日志、监控与事件复盘SSRF 事件通常不会只留下一个明显的异常请求。建议在网络出口和应用层分别记录必要信息:业务请求标识、调用用户或服务身份、目标主机的规范化表示。解析得到的地址类别,但避免记录不必要的敏感响应内容。请求是否发生重定向、最终连接地址和失败原因。响应状态、响应大小、耗时以及是否触发策略拒绝。同一身份短时间内访问大量不同主机或端口的行为。复盘时可以按时间线回答四个问题:输入从哪里进入、请求最终发往哪里、应用具有什么网络权限、响应是否影响了后续业务。不要只修补触发接口,还要搜索相同网络客户端在其他功能中的复用情况,并检查是否存在相同的出口权限。一个有效的修复,应同时降低可控性、可达性和可见性中的至少一项;更理想的方案是三者同时收敛。八、容易被忽略的注意点不要依赖黑名单拦截少量字符串。地址规范化、DNS 解析和连接策略才是关键。不要把测试环境中无法访问内网误判为漏洞不存在,生产网络拓扑可能完全不同。不要因为接口没有返回正文就认为没有风险,盲 SSRF 仍可能造成探测、回调或状态改变。不要只修复同步接口,定时任务、消息消费者和文件处理服务往往共享相同的请求逻辑。不要忽视出站代理。代理可能改变 DNS 解析位置和最终网络可达范围。不要把内部服务认证信息放在所有服务都能自动获取的环境变量或默认请求头中。总结SSRF 的分析应从单点漏洞判断转向完整请求链路分析:先追踪用户输入如何进入网络请求,再确认解析、重定向和连接阶段是否存在边界差异,最后结合真实网络权限评估影响。修复时,允许列表、规范化解析、地址分类、重定向控制和出口隔离应当组合使用,日志与复盘则用于验证修复是否覆盖了同类功能。对于长期维护的系统,最有价值的不是一次性增加规则,而是建立统一的出站访问组件、明确网络权限、持续测试异常解析路径,并在代码审计清单中固定检查协议、DNS、IPv4/IPv6、重定向、认证头和响应处理这几个环节。
推荐意见