服务端取 URL 等于代表攻击者访问网络
图片代理、Webhook 测试、网页预览和文件导入都会让服务器根据用户输入建立连接。服务器通常能访问数据库、云元数据、管理面和仅内网开放的服务,攻击者即使看不到完整响应,也可通过状态、时延或重试行为探测。SSRF 防护的目标是约束最终连接目标与响应资源,而不是用正则拒绝字符串 localhost。URL 可能经过编码、DNS、代理和重定向多次变化,每个阶段都要保持同一策略。
最先问业务是否真的需要任意互联网 URL。Webhook 目标若只允许客户预登记域名,图片导入若只来自自己的对象存储,就使用精确允许列表;允许列表比不断枚举内网黑名单稳健。不能收窄时,把抓取能力放进没有生产凭据、网络出口受限的独立服务,返回经过大小与类型限制的结果。应用主进程不应凭一段校验函数获得整个内网访问权。
IPv6 启用状态必须与策略一致。若主机网络支持 IPv6 而校验器或防火墙只处理 IPv4,攻击者可通过 AAAA 记录访问本地和保留范围;若业务完全不需要 IPv6,应在解析、连接和出口层一致禁用,而不是只丢弃 DNS 结果。每次基础设施升级重新运行双栈语料,确认代理、容器和宿主实际路由与应用判断相符。
使用标准解析器规范化协议、主机和端口
输入先限制总长度,再用 WHATWG URL 解析,拒绝解析失败、用户名密码、片段和不需要的协议。通常只允许 https,确有 http 兼容需求也明确端口范围。策略比较 URL 对象的 hostname、protocol 和有效 port,不在原字符串上 startsWith。反斜线、百分号编码、尾点、大小写、IPv6 方括号和国际化域名都由标准规范化后再判断。
显示给用户的目标与实际连接目标使用同一个规范化结果,避免界面展示可信前缀而客户端解释成另一主机。禁止 file、ftp、gopher 等非业务协议,也不让 HTTP 客户端从环境变量继承任意代理;代理本身可能访问受限网络。错误响应只说明目标不允许或获取失败,不返回解析后的内网地址、连接栈和响应片段,这些信息会增强探测能力。
DNS 解析后的每个地址都执行 IP 策略
域名字符串看似公网,仍可解析到环回、私有、链路本地、组播、保留或云元数据地址。服务端解析 A 与 AAAA 结果,把每个地址转换为规范二进制表示后逐一分类,任一不允许就拒绝,不能只检查列表第一个。IPv4 映射 IPv6、整数或非典型文本形式都要归一化。策略还应禁止应用本不需要访问的本机接口和集群服务网段。
DNS 重绑定会让校验时解析公网地址、连接时再次解析到内网。安全客户端应把已验证地址固定到实际连接,同时保留原 hostname 用于 TLS SNI 与证书校验;不能为了固定 IP 关闭证书主机验证。连接池和 DNS 缓存需要与验证结果共同设计,复用旧连接也不能跨目标。若所用库无法控制解析与连接之间的绑定,应通过受控代理或网络沙箱实现,而不是假设时间窗口足够小。
每次重定向都当作新的不可信 URL
自动跟随重定向会绕过只校验首个 URL 的实现:公网允许域可以返回 Location 指向 127.0.0.1、内网域或不同协议。客户端关闭自动跳转,逐跳解析 Location,相对于当前 URL 规范化后重新执行协议、主机、端口、DNS 和 IP 检查,并限制最大次数。循环、缺失 Location 和超限都明确失败,不能把最后一个响应当作正常内容。
允许列表场景还要决定是否可跳到列表外域。下载 CDN 可能有一组已知目标,使用精确集合;开放重定向不因为起点可信就可信。重定向后的认证头、Cookie 和用户提供头不转发到不同 origin,避免凭据外泄。日志记录跳数和目标 origin 摘要,不保存含令牌的完整 URL。测试应包含相对路径、协议相对、编码主机和多跳链。
响应预算阻止抓取器成为资源放大器
通过目标检查后仍要限制连接、首字节、总时长和最大响应字节。不能只相信 Content-Length,分块响应和压缩内容可能突破声明;流在解压前后分别设置适当上限,达到阈值立即销毁连接。只允许业务需要的内容类型,并对文件头再次识别。HTML 预览若不需要脚本,解析器禁用外部实体、子资源自动加载和主动内容,避免一次允许请求继续访问更多未校验 URL。
并发预算按用户和服务整体限制。大量慢公网目标会占满 socket、内存和 DNS 线程,即使每个 URL 都合法。队列满时拒绝或延后,不让 Web 请求无限等待。抓取结果缓存键包含规范 URL 与安全策略版本,私有或带认证结果不进入公共缓存。响应正文不写普通日志,错误页可能包含内部回显或攻击载荷;只记录字节、类型、耗时和结果码。
网络出口策略是应用校验失误后的第二道墙
运行抓取器的账户或容器在网络层拒绝云元数据、数据库、控制平面、节点网段和其他敏感服务,只允许必要 DNS 与公网出口,最好经统一代理执行相同地址策略。应用连接串、云凭据和宿主 socket 不注入抓取进程,文件系统也只提供临时工作区。这样即使 URL 解析出现新绕过,攻击影响仍被限制在低权限故障域。
出口代理不能成为开放转发器,调用者使用受认证的服务身份,代理重新解析和记录规范目标。网络规则既覆盖 IPv4 也覆盖 IPv6,集群 service、宿主网关和本地解析器都纳入测试。健康检查不应要求抓取器访问原本禁止的管理地址。规则变更和云网段调整进入发布流程,不能只在首次部署手工配置后长期无人验证。
测试语料覆盖解析差异与连接时变化
单元语料包括环回、私网、链路本地、IPv4 映射 IPv6、尾点域名、国际化域名、用户名混淆、异常端口、编码字符和不支持协议。断言基于解析后的目标,不只比较函数返回布尔值。集成测试控制 DNS 服务器,先返回公网再返回内网,确认连接固定在已验证地址;TLS 证书主机不匹配必须失败,不能因为使用固定 IP 而关闭验证。
HTTP 测试服务器构造多跳重定向、慢响应、虚假长度、压缩膨胀和错误内容类型。网络层从抓取容器实际尝试连接元数据、数据库与控制面,必须不可达;允许公网样例正常完成。并发压测观察 socket、内存、DNS、队列与取消后的资源归零。被拒绝请求不能产生任何后续子资源访问,错误响应和日志也不能泄露解析出的内部地址。
运行期按目标类别观察而不泄露完整 URL
指标统计允许、解析拒绝、IP 拒绝、重定向拒绝、超时、超限和内容拒绝,按受控域类别而不是完整 URL 分组,避免高基数和敏感查询进入监控。拒绝率或 DNS 结果突然变化可能是攻击、供应商配置变化或解析故障,值班人员根据类别调查。外部依赖不可用时保持原业务状态并安全重试,不能为了可用性临时关闭 IP 检查。
允许列表、端口、出口规则和解析库升级都触发安全回归。定期检查抓取服务是否新获得数据库凭据、宿主挂载或更宽网段,权限漂移会削弱隔离。上线新抓取用途先独立定义目标与响应预算,不复用一个“万能下载 URL”接口。可靠 SSRF 防线由业务收窄、标准解析、解析后 IP 判断、连接绑定、逐跳验证、资源限制和网络出口共同构成。
Webhook 与回调验证不能借机访问任意目标
Webhook 注册可把目标分成配置阶段和投递阶段。配置时完整执行 URL、DNS、IP 与重定向策略,发送一次带随机挑战的验证请求;只有远端按协议回显才激活。挑战证明调用者能控制目标应用,却不证明目标永远是公网地址,因此每次投递仍重新解析并执行网络策略,不能永久信任初次结果。
投递不携带用户 Cookie、内部认证头或任意调用者提供的 header。签名密钥按 webhook 独立生成,签名覆盖时间、事件 id 与正文,接收方可验证重放;请求目标变化时重新验证并轮换必要凭据。响应只读取有限字节用于状态判断,不把远端页面原样展示在高权限后台,避免存储型内容和敏感反射。
失败重试保持同一事件操作 id,限制总次数和最长生命周期。DNS/IP 拒绝属于安全终止,不因“之前成功过”继续发送;429 与短暂 5xx 才按退避。后台展示规范化 origin、最后解析类别、状态和下次时间,隐藏 URL 中可能遗留的用户信息。人工“立即重试”仍走完整解析与出口代理,不能绕过策略。
若客户必须连接私有网络,应使用明确的私有连接产品、双向认证和租户级网络映射,而不是放宽公共 URL 接口允许任意 RFC1918 地址。私有连接执行器与公网抓取器分离凭据和路由,每个租户只见自己的目标。安全模型从任意 URL 变成预配置资源 id,审计和撤销也更清晰。
实现片段
const target = await resolveAndValidate(new URL(input), egressPolicy)
评论 · 0