跳转到帖子

背景SSRF(Server-Side Request Forgery,服务端请求伪造)在实际漏洞挖掘和代码审计里出现频率不低。它的本质不是“让浏览器访问某个地址”,而是诱导服务端以自己的网络位置、权限和出口策略去访问攻击者指定的资源。这类问题的危险点在于:服务端通常位于内网、云环境或受信任网段,能访问到普通用户无法直接访问的接口,例如内部管理服务、元数据服务、未鉴权的 RPC、运维面板等。本文不讨论攻击利用链,只从安全研究和防护视角整理一次典型 SSRF 风险的复现思路、代码审计要点和修复建议。本文示例均以本地测试环境为前提,重点是识别风险与加固,不提供针对真实目标的攻击操作。典型场景SSRF 常见于以下业务功能:远程图片抓取、头像上传、文章封面解析URL 预览、网页标题抓取、链接转码PDF/截图生成服务,例如传入 URL 后服务端渲染页面Webhook 回调测试、HTTP API 调试工具文件导入功能,例如支持从 URL 拉取 CSV、XML、JSON从代码层面看,只要存在“用户可控 URL”并由服务端主动请求,就应该按 SSRF 风险处理。问题代码示例下面是一段简化后的 Java 代码,业务逻辑是根据用户传入的 URL 下载图片并保存。问题在于它直接信任了外部输入,没有对协议、目标地址、跳转行为做限制。public byte[] fetchImage(String url) throws IOException {
URL target = new URL(url);
HttpURLConnection conn = (HttpURLConnection) target.openConnection();
conn.setConnectTimeout(3000);
conn.setReadTimeout(5000);
conn.setInstanceFollowRedirects(true);

try (InputStream in = conn.getInputStream()) {
return in.readAllBytes();
}
}这类代码在审计时需要重点关注几个点:是否允许用户控制完整 URL是否限制协议,例如只允许 http/https是否检查目标 IP 是否为内网、回环、本机地址是否跟随 30x 跳转,跳转后的地址是否重新校验是否存在 DNS 重绑定风险是否对响应大小、超时时间、Content-Type 做限制本地复现思路安全测试时建议搭建完全隔离的本地环境。可以准备两个服务:一个模拟业务服务,一个模拟内部服务。业务服务负责接收用户传入 URL 并发起请求;内部服务只监听本机或内网地址,用来观察是否被服务端访问。例如启动一个仅用于测试的本地 HTTP 服务:python3 -m http.server 8081 --bind 127.0.0.1如果存在 SSRF,业务服务在处理如下 URL 时可能会访问到本机内部端口: 的过滤难点在于 URL 的表现形式非常多,不少简单的字符串黑名单会被绕过。常见问题包括:只判断是否包含 127.0.0.1,但没有处理 localhost、IPv6、整数 IP、八进制或十六进制表示只校验第一次请求地址,没有校验 302 跳转后的地址解析 URL 与真正发起请求的库不一致,导致校验结果和请求结果不同只校验域名字符串,没有解析最终 IPDNS 解析结果未固定,请求前后解析结果可能变化没有限制端口,导致访问到非 HTTP 管理端口举个常见审计点:代码里如果出现如下逻辑,基本不能认为是安全防护。if (url.contains(\"127.0.0.1\") || url.contains(\"localhost\")) {
throw new IllegalArgumentException(\"blocked\");
}这种方式只能挡住最直观的输入,无法覆盖地址编码、跳转、DNS 变化等情况。SSRF 防护更适合采用白名单和解析后校验,而不是依赖零散黑名单。更稳妥的防护思路实际落地时,建议优先从业务设计上减少“任意 URL 拉取”。如果必须支持外部 URL,可以按以下策略处理。协议白名单:只允许 http 和 https,禁止 file、gopher、ftp、dict 等协议域名白名单:对固定业务来源使用明确白名单,不允许任意域名IP 校验:解析域名后检查所有解析结果,拒绝内网、回环、本机、链路本地地址跳转校验:禁止自动跳转,或每次跳转后重新执行完整校验端口限制:只允许 80、443 或业务明确需要的端口响应限制:限制响应大小、下载时间、Content-Type,避免资源消耗网络隔离:让抓取服务运行在受限网络环境,不能访问管理网段和云元数据地址下面是一段偏防御思路的伪代码,重点是体现校验顺序,而不是作为可直接复制的完整库函数。URI uri = URI.create(inputUrl);

if (!List.of(\"http\", \"https\").contains(uri.getScheme())) {
throw new IllegalArgumentException(\"unsupported scheme\");
}

if (uri.getPort() != -1 && !List.of(80, 443).contains(uri.getPort())) {
throw new IllegalArgumentException(\"unsupported port\");
}

InetAddress[] addresses = InetAddress.getAllByName(uri.getHost());
for (InetAddress address : addresses) {
if (address.isAnyLocalAddress()
|| address.isLoopbackAddress()
|| address.isLinkLocalAddress()
|| address.isSiteLocalAddress()) {
throw new IllegalArgumentException(\"private address denied\");
}
}

// 发起请求时建议关闭自动跳转,手动处理 Location 并重新校验
HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NEVER)
.connectTimeout(Duration.ofSeconds(3))
.build();需要注意,Java 的 isSiteLocalAddress() 能覆盖部分私有地址,但在云环境、容器网络、IPv6、特殊地址段下仍建议结合明确 CIDR 规则维护 deny list,例如 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、fc00::/7、fe80::/10 等。代码审计检查清单做代码审计时,可以从入口、校验、请求、部署四个层面检查。检查点关注内容输入入口是否存在用户可控 URL、Host、Callback、Webhook、ImageUrl 等参数协议处理是否仅允许 http/https,是否禁用本地文件和其他非预期协议地址解析是否解析最终 IP,是否拒绝内网、回环、链路本地地址跳转行为是否自动跟随 30x,跳转后的 Location 是否重新校验请求限制是否设置连接超时、读取超时、最大响应体大小网络边界应用所在容器或主机是否能访问管理网段、元数据服务日志审计是否记录目标域名、解析 IP、状态码、失败原因,便于排查异常修复中的常见误区只做字符串黑名单:容易漏掉编码、IPv6、跳转、域名解析等情况。只限制域名后缀:例如简单判断 endsWith(\"example.com\"),如果没有正确处理点号边界,可能产生误判。信任请求库默认行为:不同语言和库对跳转、代理、DNS、URL 解析的处理并不一致。忽略出网策略:应用层修复很重要,但网络层如果完全放开,风险面仍然较大。没有回归测试:SSRF 修复后建议补充单元测试和集成测试,覆盖内网地址、跳转、异常协议等样例。安全测试建议在授权测试中,不建议对目标内网做大范围端口探测。更合理的方式是:使用自建回显服务确认服务端是否发起请求用本地或测试环境模拟内网地址访问控制请求频率和超时时间,避免造成业务阻塞优先验证防护逻辑是否存在缺陷,而不是扩大探测范围测试记录中保留请求参数、时间、日志证据和影响说明如果企业环境中使用云服务,还应重点检查应用实例是否能访问元数据服务。生产环境可以通过云厂商推荐方式限制元数据访问,或使用更严格的实例元数据版本和访问策略。总结SSRF 的核心风险在于服务端请求能力被外部输入间接控制。它往往不是单点漏洞,而是业务设计、URL 解析、请求库行为、网络隔离共同作用后的结果。从长期治理角度看,比较有效的做法是:业务上减少任意 URL 抓取;代码上采用协议和域名白名单、解析后 IP 校验、跳转重校验;运行环境上收紧出网权限;测试上补齐回归用例。这样即使某一层出现疏漏,也不至于直接暴露内部服务。

代码审计与漏洞定位示意图
代码审计、调用链与关键函数定位示意

0篇意见

推荐意见

没有意见。

游客
抱歉,你的帖子内容包括我们不允许的字词。请编辑你的帖子,删除下面高亮的屏蔽字。
添加意见…