跳转到帖子

SSRF(Server-Side Request Forgery,服务端请求伪造)经常被简单理解为“用户可以控制一个 URL”。但在真实项目中,风险是否成立,取决于请求发起位置、网络可达范围、协议处理方式、重定向策略以及响应数据是否会回传给用户。因此,SSRF 审计不能只搜索 requests.get()、curl_exec() 或类似调用,更应该沿着“输入如何进入请求组件、请求能访问什么、结果如何被使用”这条链路分析。本文以代码审计和修复验证为主线,整理一套适用于 Web 应用、内部接口和微服务的排查方法。一、典型场景与风险边界常见业务功能包括:根据 URL 抓取网页标题、生成摘要或下载远程文件。Webhook、回调地址、图片代理、RSS 导入等服务端主动访问功能。云平台中的元数据查询、健康检查和服务连通性测试。后台管理系统中的远程配置导入、在线预览和 URL 校验。如果服务端能够访问用户无法直接访问的网段,且请求结果会回显、转发或影响后续业务,就可能形成 SSRF。影响通常分为三类:网络访问:服务端成为访问内部服务的跳板,可能接触管理接口、调试端口或内部 API。信息泄露:响应正文、状态码、响应时间或错误信息暴露内部网络信息。业务联动:内部接口存在高权限操作时,SSRF 可能进一步影响配置、任务或凭据安全。需要注意的是,单纯“能让服务器发起请求”并不等于高危。审计时应同时确认请求权限、网络隔离、响应可见性和后续影响,避免只根据参数名称或单个测试现象下结论。二、代码审计:先建立完整数据流第一步不是尝试构造特殊 URL,而是从入口建立数据流图。重点关注以下位置:HTTP 参数、JSON 字段、表单字段和消息队列中的 URL。数据库中保存后再由定时任务处理的地址。HTTP 头部中的回调地址、来源地址或自定义扩展字段。经过 URL 解码、模板渲染、拼接或重定向后才进入请求库的值。搜索调用点时,可以先使用静态检索定位高风险 API,再向上追踪参数来源,向下确认响应去向。例如 Python 项目可关注 requests、urllib、httpx;Java 项目可关注 URL、HttpClient、RestTemplate、WebClient;Go 项目可关注 http.Client、http.Get 和自定义 Transport。rg \"requests\\.(get|post|request)|http\\.Get|RestTemplate|WebClient\" src/定位调用后,建议记录以下信息:审计项需要确认的问题输入来源是否完全或部分可控?是否经过解码、拼接和模板处理?协议范围是否只允许 HTTP/HTTPS?是否会处理文件、字典、脚本等非预期协议?目标解析是否校验主机名、端口、IPv4、IPv6 及解析结果?网络位置运行容器、主机或代理能访问哪些网段?重定向是否自动跟随?每次跳转后是否重新执行策略校验?响应处理正文、状态码、错误信息和响应头是否返回给用户?三、为什么简单黑名单经常失效常见修复方式是拒绝 127.0.0.1、localhost 或少数私有地址。这种方式覆盖面有限,主要问题有以下几类。1. 主机名与 IP 的判断不一致应用可能先对字符串做校验,之后才进行 DNS 解析。这样会产生“校验对象”和“实际连接对象”不一致的问题。域名解析结果还可能因 DNS 轮询、缓存或配置变化而变化。2. 地址表示形式复杂IPv4、IPv6、IPv4 映射地址、十进制或十六进制表示,以及带端口和用户信息的 URL,都可能让字符串匹配失效。安全判断应基于标准 URL 解析器和规范化后的 IP 对象,而不是简单的字符串包含关系。3. 重定向绕过初始检查即使初始地址是公开站点,服务端也可能自动跟随跳转到受限制地址。如果客户端开启了自动重定向,策略应在每个跳转目标上重新执行,而不是只校验第一跳。4. 解析器差异不同语言和库对空白字符、反斜杠、端口、用户名密码、国际化域名的解析规则并不完全一致。安全校验使用的解析器,应该尽量与实际发起连接的组件保持一致,避免出现“校验认为安全,连接组件解析出另一目标”的情况。四、一个更稳妥的校验思路安全校验可以拆成四层,而不是依赖单一黑名单:协议层:只允许业务必需的协议,通常为 http 和 https。语法层:拒绝缺少主机、包含用户信息、异常端口或不符合业务格式的 URL。网络层:解析所有地址,并拒绝环回、链路本地、私有、保留和未分配地址。连接层:限制超时、响应大小、跳转次数,并在每次连接前重新确认目标。下面是一个用于说明审计思路的 Python 片段。它只展示地址解析和网络范围判断,不应直接替代生产环境的 HTTP 客户端封装。from ipaddress import ip_address
from urllib.parse import urlparse
import socket

def is_forbidden_ip(value):
ip = ip_address(value)
return (
ip.is_private or
ip.is_loopback or
ip.is_link_local or
ip.is_reserved or
ip.is_multicast or
ip.is_unspecified
)

def validate_target(raw_url):
parsed = urlparse(raw_url)

if parsed.scheme not in ('http', 'https'):
raise ValueError('unsupported scheme')

if not parsed.hostname:
raise ValueError('missing host')

if parsed.username or parsed.password:
raise ValueError('userinfo is not allowed')

port = parsed.port
if port is not None and not (1 <= port <= 65535):
raise ValueError('invalid port')

addresses = socket.getaddrinfo(
parsed.hostname,
port or (443 if parsed.scheme == 'https' else 80),
type=socket.SOCK_STREAM
)

for item in addresses:
ip = item[4][0]
if is_forbidden_ip(ip):
raise ValueError('target address is not allowed')

return parsed这段代码仍然存在 DNS 解析与实际连接之间的竞态问题。生产环境更推荐采用固定出口代理或专用抓取服务,在网络层限制可访问的域名和网段;如果必须由应用直接连接,则应使用能够绑定解析结果、控制连接目标并对重定向重新校验的统一网络组件。五、修复时容易遗漏的关键点1. 不要把“公网域名”当作安全边界允许某个域名后,仍需考虑该域名解析结果变化、解析到私网地址、DNS 配置错误以及云环境中的内部解析区域。域名白名单适合明确的业务依赖,但仍应结合解析结果和出口网络策略。2. 关闭或严格控制自动重定向如果业务不需要重定向,可以直接关闭。必须支持重定向时,应限制最大跳转次数,并在每一跳重新进行协议、主机、端口和 IP 范围检查,同时禁止从 HTTPS 降级到 HTTP,除非业务有明确需求。3. 限制请求能力设置连接超时、读取超时和整体请求超时。限制响应体大小,避免把远程大文件直接读入内存。限制响应头数量和单个头部长度。限制允许的端口和请求方法,默认优先使用 GET。不要把内部请求中的 Cookie、Authorization 等凭据透传给用户指定目标。4. 防止错误信息泄露直接返回底层异常可能泄露解析地址、内网主机名、代理信息或连接细节。对外返回统一错误码,对内记录结构化日志,避免把完整响应正文和敏感请求头写入普通日志。5. 结合网络隔离,而不是只依赖代码应用层校验存在实现差异和遗漏的可能。容器、虚拟机和云主机应通过安全组、网络策略、出口代理或防火墙限制访问范围。即使应用校验失效,网络层也应阻止访问管理网段、控制面地址和不必要的内部服务。六、如何在本地安全验证修复效果验证应在隔离的本地实验环境或授权测试环境进行,不要把生产内网地址作为测试目标。可以准备一个公开测试服务和一个本地模拟服务,用于观察以下行为:直接访问 HTTP 和 HTTPS 是否符合预期。私有地址、环回地址、链路本地地址是否被统一拒绝。域名解析到受限制地址时是否拒绝。公开地址跳转到受限制地址时是否在跳转阶段拦截。超时、大响应和异常协议是否受到限制。错误响应是否不会泄露底层网络细节。测试结果最好使用表格记录,而不是只保留某次手工请求的截图:用例预期结果实际结果备注允许的业务域名请求成功记录实际行为确认证书和跳转策略本地回环地址拒绝记录状态码和日志不记录敏感正文私有网段地址拒绝记录状态码和日志覆盖 IPv4、IPv6多次重定向超出限制后拒绝记录跳转次数每一跳重新校验超时或超大响应主动终止确认资源释放检查线程和连接池七、代码审计中的判断标准一个完整的 SSRF 结论至少应包含四部分:可控输入、危险请求点、实际可达范围和可观察影响。只发现“URL 进入 HTTP 客户端”时,建议先标记为待确认问题,继续核实以下内容:调用是否只访问固定域名,还是完全接受用户输入。是否存在统一的网络访问封装和集中校验。部署网络是否能够访问敏感网段。请求结果是否被返回、缓存、写入文件或触发后续任务。是否存在身份凭据、代理配置或高权限网络身份。漏洞等级不应仅依据参数名判断。一个只能访问固定公开站点、无法回显正文且有严格出口限制的功能,与一个可以访问任意内部地址并完整返回响应的接口,风险差异很大。八、修复后的回归检查清单所有 URL 入口是否都经过同一套校验逻辑。协议、主机、端口和解析地址是否采用明确的允许策略。IPv4、IPv6、域名解析和异常 URL 语法是否覆盖测试。重定向的每一跳是否重新校验。DNS 解析与实际连接之间是否存在可利用的竞态。请求超时、响应大小、连接池和并发数是否有上限。敏感请求头是否不会被透传。日志和错误信息是否经过脱敏。网络层是否限制了应用的实际出口范围。监控是否能够发现异常目标、异常端口和大量失败请求。总结SSRF 的核心不是某个特殊 URL 写法,而是服务端是否在缺乏约束的情况下替用户访问了不应访问的网络资源。可靠的治理方式应当从代码、请求组件、运行环境和网络出口四个层面同时考虑。在审计阶段,先建立完整数据流,再确认协议、解析、重定向和响应处理;在修复阶段,优先采用允许列表、统一网络访问组件和出口隔离;在验证阶段,使用隔离环境覆盖地址表示、DNS、跳转、超时和响应限制等边界条件。这样得到的结论更接近真实风险,也更容易转化为可长期维护的工程方案。

服务器与云环境安全示意图
服务器、云资产与权限边界梳理示意

0篇意见

推荐意见

没有意见。

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