SSRF(Server-Side Request Forgery,服务端请求伪造)并不是“让服务器发起一次 HTTP 请求”这么简单。它真正危险的地方在于:请求行为发生在受信任的服务器网络环境中,攻击者可能借此访问外部系统无法直接访问的管理接口、云平台元数据服务、内部调试端点,甚至触发带有副作用的业务接口。
在实际审计中,SSRF 往往不是单个函数的问题,而是输入校验、URL 解析、网络出口、重定向处理和权限边界共同失效的结果。下面以代码审计和修复为主线,整理一套适用于常见 Web 服务的分析方法。
一、典型业务场景
以下功能通常值得优先检查:
- 根据用户提交的 URL 抓取网页标题、下载远程图片或导入 RSS。
- 在线生成缩略图、PDF、网页快照或 Markdown 预览。
- Webhook、回调地址、第三方接口连通性测试。
- 代理服务、URL 跳转、远程文件校验和云存储导入。
- 后台提供的“检测目标地址”“探测服务状态”等运维功能。
审计时不要只搜索 requests.get、HttpClient 或 curl。更有效的方式是从外部可控输入出发,沿数据流追踪到网络请求函数,确认中间是否存在可靠的协议、主机、端口和重定向控制。
二、风险形成的关键链路
一个常见的易受影响实现如下:
String target = request.getParameter("url");
HttpGet httpGet = new HttpGet(target);
return httpClient.execute(httpGet);这段代码的问题不只在于缺少黑名单。即使增加了对 127.0.0.1 的字符串判断,也可能被以下情况绕过或误判:
- 使用十进制、十六进制或 IPv6 形式表示同一地址。
- 域名首次解析到公网地址,随后在请求过程中解析到内网地址,形成 DNS 重绑定风险。
- 允许 HTTPS 但未限制重定向,服务端最终访问了不受信任的协议或地址。
- 只校验 URL 字符串中的主机部分,却没有验证实际连接的 IP。
- 解析器对用户名、端口、片段和编码的处理与业务代码理解不一致。
因此,SSRF 防护应当是“解析后判断 + 连接时约束 + 出口限制”的组合,而不是简单的字符串过滤。
三、代码审计的实用步骤
1. 确认输入是否真正可控
先记录参数来源,包括查询参数、JSON 字段、表单字段、消息队列内容和数据库中可被用户间接影响的配置。重点确认是否存在二次存储或异步任务,因为有些风险并不会在当前请求中直接发起网络访问。
同时检查输入是否经过了 URL 解码、模板渲染或拼接。审计报告中应明确给出数据流,例如“request.body.url → normalizeUrl() → fetch()”,避免只报一个危险函数名。
2. 识别请求能力
需要确认服务端请求支持哪些能力:
- 支持哪些协议,例如 HTTP、HTTPS、FTP 或本地文件协议。
- 是否自动跟随 3xx 重定向。
- 是否可以自定义请求头、请求方法和请求体。
- 是否允许访问非标准端口。
- 连接超时、读取超时和响应大小是否有限制。
- 请求是否运行在高权限网络环境或具备云平台访问凭证。
其中,支持非 HTTP 协议和自定义请求方法会显著扩大影响面。即使当前业务只需要读取网页,也不应默认继承底层客户端的全部协议能力。
3. 检查地址解析与连接时机
安全校验应围绕“最终连接到哪里”展开,而不是只验证用户输入的域名。建议在解析后获取所有 A 和 AAAA 记录,分别判断其是否属于禁止访问的地址范围,并在实际建立连接时继续执行约束。
常见的禁止范围包括回环地址、链路本地地址、私有地址、未指定地址、组播地址以及企业内部明确划定的管理网段。IPv4 和 IPv6 都要覆盖,不能只处理 10.0.0.0/8 等少数 IPv4 网段。
四、一个更稳妥的防护思路
下面是伪代码,重点展示校验顺序,不对应某个具体框架:
parsed = parse_absolute_url(input)
if parsed.scheme not in ["http", "https"]:
reject("unsupported scheme")
if parsed.username or parsed.password:
reject("userinfo is not allowed")
if parsed.port not in [80, 443, null]:
reject("unsupported port")
addresses = resolve_all(parsed.hostname)
if addresses is empty:
reject("host cannot be resolved")
for address in addresses:
if is_loopback(address) or is_private(address) or is_link_local(address):
reject("destination is not allowed")
response = fetch_with_pinned_addresses(
url=parsed,
addresses=addresses,
follow_redirects=false,
connect_timeout=3,
read_timeout=5,
max_bytes=2 * 1024 * 1024
)这里有几个容易被忽略的细节:
- 必须使用结构化 URL 解析器,不要通过字符串查找判断主机名。
- 禁止用户名和密码部分,避免出现视觉混淆以及日志泄露。
- 对重定向默认关闭;如果业务必须支持,应对每一次跳转重新执行完整校验。
- 读取响应时限制最大字节数,避免被大响应拖垮内存或连接池。
- 限制请求方法、请求头和请求体,业务不需要的能力不要开放。
- 在网络层使用出站防火墙或 egress proxy 进行第二道控制,避免应用层遗漏造成单点失效。
五、重定向、DNS 重绑定和代理的处理
重定向
很多 HTTP 客户端默认跟随重定向,导致第一次校验通过的公网地址最终跳转到内部地址。更安全的做法是关闭自动跳转,由业务层读取并解析 Location,对新地址重新执行协议、域名、IP 和端口校验。
DNS 重绑定
如果应用每次连接前都会重新解析域名,攻击者可能利用域名解析结果变化,使校验阶段和连接阶段得到不同地址。防护上应尽量让解析、校验和连接使用同一组经过校验的地址,并通过连接器或代理固定目标。对于高风险场景,允许访问的域名应使用静态白名单,且不要仅依赖运行时 DNS 结果。
代理环境
需要确认 HTTP 客户端是否自动读取 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY 等环境变量。代理配置可能改变实际请求路径,也可能让原本的地址校验失效。生产环境应明确代理策略,并在测试中验证直连和代理两种路径。
六、安全测试与验证方法
测试应在授权的本地实验环境或专用测试网段进行,不要把内部地址、云元数据地址或第三方站点作为生产验证目标。建议准备一个受控的 HTTP 服务,记录客户端的请求方法、请求头、解析地址和跳转过程。
可以先验证以下行为:
- 不接受缺少主机的相对 URL。
- 不接受非 HTTP、HTTPS 协议。
- 不接受带用户名密码的 URL。
- 不允许访问本地实验网段、回环地址和链路本地地址。
- 重定向到禁止地址时请求被阻断。
- 连接超时、响应过大或响应头异常时能够及时结束。
- IPv6 地址和域名解析到多个地址时,所有地址都经过检查。
若使用命令行验证客户端行为,应优先针对自建测试服务:
curl --max-time 5 --location --max-redirs 0 \
--data-urlencode 'url= \
URL 直接写入日志。检测规则可以关注以下信号:
- 短时间内大量请求不同主机或非业务常用端口。
- 访问地址频繁出现在本地、私有或链路本地范围。
- 同一请求出现多次重定向或协议变化。
- 服务端请求耗时明显上升,连接池和文件描述符持续增长。
- 应用容器或主机出现异常的 DNS 查询、代理连接或出站流量。
如果已经确认存在风险,应先限制出站访问并保留应用、DNS、代理和防火墙日志,再检查是否有敏感响应被返回给调用方。对于运行在云环境中的服务,还应核查实例角色权限、临时凭证使用情况以及同一网络中的横向访问记录。
八、常见错误做法
错误做法 问题 改进方向 只拦截 127.0.0.1 遗漏 IPv6、私有地址、链路本地地址和地址变形 使用标准 IP 库统一解析并覆盖完整地址类别 只校验 URL 文本 无法保证实际连接目标未发生变化 在解析和连接阶段都执行约束,并配合出口控制 默认跟随重定向 校验通过的地址可能跳转到禁止地址 关闭自动跳转,逐跳重新校验 依赖黑名单 规则难以穷尽,维护成本高 优先采用协议、域名和端口白名单 只修复业务接口 后台任务、Webhook 或导入功能可能仍然复用同一请求组件 统一封装安全请求组件,集中配置策略
九、总结
SSRF 的本质是外部输入获得了不应有的网络访问能力。有效治理需要从代码、解析器、HTTP 客户端、网络出口和监控审计多个层面同时入手。
在代码审计中,建议优先回答三个问题:目标地址是否由不可信输入决定;最终建立连接的 IP 是否经过可靠校验;重定向、DNS 变化和代理是否可能改变真实访问目标。修复时则应坚持最小权限原则,只开放业务真正需要的协议、域名、端口和响应能力,并通过网络层策略提供兜底保护。
安全测试应始终在明确授权和可控环境中进行。对 SSRF 这类可能触及内部网络的漏洞,验证“是否被正确阻断”通常比访问真实内部服务更重要。

服务器、云资产与权限边界梳理示意
推荐意见