题目与适用场景
请为一个 URL 预览 API 设计安全抓取链路。已登录用户可以提交任意公网 HTTP 或 HTTPS URL, 后台任务抓取文档并只返回经过清理的页面标题。产品的输入就是公开网站,因此不能维护一份固定域名 白名单。
本题只允许默认的 80 和 443 端口,最多跟随 3 次重定向,整个任务总时限为 3 秒,解压后最多读取 2 MiB。这些数字是面试假设,不是通用安全标准。服务不得通过 IPv4 或 IPv6 访问回环、私网、共享、 链路本地、组播、保留、文档或云元数据目标;还要应对特殊 IP 写法、混合 DNS 结果、DNS rebinding、 公网 URL 跳转内网、超大响应和慢速服务器。
MITRE 对 CWE-918 的定义是:服务器收到 URL 并发起请求,却没有充分确认请求是否到达预期目标。 这个定义点明了题目的核心——出站授权。输入校验、DNS 行为、网络路由和最终套接字目的地址必须执行 同一套策略。一则 2026 年公开 Web 安全面试讨论把 SSRF 与 XSS、SQL 注入、IDOR 一起列为候选人应 准备说明的主题。这能证明题目仍有当前面试语境,不能据此声称某家公司固定出题或高频出现。 OWASP Top 10:2025 已把 CWE-918 纳入 Broken Access Control,也支持用授权问题来理解 SSRF。
现有题库在爬虫和短链接系统中各用一小段提到 SSRF。本题单独考察抓取链路:URL 解析、DNS 解析、 目的地址分类、连接绑定、重定向、出站控制,以及如何证明策略不会被检查时与使用时之间的竞态绕过。
面试官考察什么
第一项是候选人能否把目的地址当作授权决策。只拦截字符串 localhost 和 127.0.0.1 不完整。 目标可能是 IPv6 字面量、IPv4-mapped IPv6、其他数字写法、同时返回安全和危险地址的域名、重定向 目标,或者在校验与连接之间改变 DNS 结果的域名。
第二项是解析器纪律。正则和子串匹配不能定义 URL 语义。高质量回答会统一使用经过验证的标准解析器, 拒绝解析失败和内嵌凭据,只允许明确的 HTTP 协议与端口,并且后续所有决策都取自解析器的规范字段。 即使套接字绑定到已批准的 IP,TLS 仍应使用原始规范域名做 SNI 和证书主机名校验。
第三项是能否封住 DNS rebinding 的空窗。先解析并批准一个 IP,随后 HTTP 客户端在建连前再次查询 DNS,这次校验就没有意义。抓取器必须连接到指定的已批准地址,或交给能完成同样动作的策略化出站 代理。每次重定向和重试都产生一次新的授权决策。
第四项是纵深防御。应用层即使出现遗漏,网络层也应无法路由到内网和元数据地址。工作进程使用最小 权限身份,不携带用户 Cookie 或授权头;解析过程有资源上限,也不会执行页面脚本或加载子资源。
最后一项是可证伪的验证。测试必须覆盖特殊地址写法、混合 A/AAAA 记录、二次解析竞态、重定向、 解压上限和出站防火墙。笼统地说“加白名单”不满足本题,因为任意公网域名就是产品需求;白名单适合 另一个只接入固定合作方的场景。
回答前要确认的问题
- 目标是任意公网网站还是固定合作方? 固定合作方可以使用精确的协议、域名和端口白名单。任意
URL 预览需要保守的公网地址策略,以及由网络层强制执行的出站边界。
- 需要哪些协议、端口、方法和请求头? 本题只允许 HTTP 使用 80 端口、HTTPS 使用 443 端口发
GET,不接受调用方指定方法、请求体、代理、Cookie、授权头或任意请求头。
- 是否需要跟随重定向? 需要,最多 3 跳。关闭自动跟随,每个
Location目标都独立授权后再访问。 - 最终需要什么输出? 只需要清理后的标题。工作进程不返回原始 HTML,不执行 JavaScript,不解析
XML 外部实体,也不抓取图片、样式表、字体或脚本。
- 工作进程能路由到哪些地址? 回答要覆盖 IPv4、IPv6、容器与服务网络、公司内网和云厂商元数据
地址。先盘点有效路由,才能验证出站策略。
- 缓存和重试如何处理? 缓存可减少重复抓取,但未命中仍走完整安全链路。本设计默认不自动重试;
若显式重试,必须从 DNS 解析、授权和连接绑定重新开始。
- 可用性如何取舍? 如果域名返回的任一地址不安全,本题拒绝整个目标。这可能挡住配置错误的公开
网站,但策略会稳定地失败关闭,不会悄悄换用另一条解析结果。
30 秒回答框架
“我会用标准 URL 解析器,限定 HTTP 使用 80 端口、HTTPS 使用 443 端口,并拒绝内嵌凭据。域名通过 受控解析器查询全部 A 和 AAAA 结果;IPv4-mapped IPv6 先还原,只要一个地址不符合持续维护的公网 策略就拒绝目标。工作进程直接连接已批准 IP,但保留原域名做 Host、TLS SNI 和证书校验,封住 DNS rebinding 空窗。关闭自动重定向和重试,每一跳都重做完整检查;隔离的出站防火墙再阻止内网和元数据 路由。最后落实 3 秒、2 MiB、3 跳上限,不加载子资源,并端到端测试 rebinding 和跳转元数据地址。”
分步深入分析
第一步:把需求写成一套目的地址策略
把规则集中表达,避免 API、工作进程和 HTTP 客户端各写一套字符串判断:
DestinationPolicy
schemes: http, https
origin_pairs: http:80, https:443
userinfo: forbidden
address_requirement: globally reachable public unicast
redirects: at most 3, authorize every hop
method: GET
caller_headers: none
total_deadline: 3 seconds
decompressed_body_limit: 2 MiBAPI 负责认证调用方、执行账户和租户额度、把原始 URL 当作不可信数据保存并投递任务。不要在 API 中 做一次“安全校验”后把可信标志传给稍后执行的工作进程;任务运行前 DNS 和路由都可能变化。真正打开 连接的是工作进程,因此它必须在建连前做权威决策。
固定合作方的最强策略是精确列出规范协议、域名和端口,必要时再限制路径。本题必须访问任意公网主机, 所以只允许被分类为公网且全局可达的目的地址。分类数据应来自权威地址注册表和组织自己的网络清单。 手写一份只有 RFC 1918 的列表会漏掉回环、链路本地、共享、组播、保留、文档、IPv6 唯一本地、 IPv4-mapped 形式以及云厂商特定的元数据地址。
第二步:先解析 URL,后续只使用解析字段
使用平台的 WHATWG 兼容解析器或同等成熟的 URL 解析器,并要求绝对 URL。解析失败、带用户名或密码、 产品不需要的 fragment、非 HTTP 协议以及策略外端口都直接拒绝。国际化域名也交给解析器规范化,后续 不能再用正则重新解释原始字符串。
https://expected.example@evil.example/ 说明了前缀匹配的错误:真正建连的主机是 evil.example。fragment 不决定网络目标,编码和特殊数字形式也可能让校验器与 HTTP 客户端产生 不同理解。协议、规范主机、有效端口和请求路径必须来自同一个解析结果。
如果主机本身就是 IP 字面量,立即规范化并分类。IPv4-mapped IPv6 要先取出内嵌 IPv4,再同时套用 两类地址策略。不能根据标点、字符串长度或“看起来像域名”来判断安全性。
第三步:校验全部解析地址,并把连接绑定到批准结果
域名通过受控解析器查询 A 与 AAAA 记录,按正常 DNS 过程处理 CNAME,并收集最终地址。本题只要出现 一个策略禁止的结果,就拒绝整个目标。每个地址和 IP 字面量使用同一分类器。拒绝时记录原因码,不要 记录可能带查询秘密的完整 URL。
核心不变量是:
策略批准的 IP == connect() 实际使用的 IP从已批准结果中选择一个 IP,并把这个确切地址交给套接字层。HTTP Host 仍使用规范域名;HTTPS 的 TLS SNI 和证书主机名校验也使用域名,不能用一张只对数字 IP 有效的证书替代。禁止 HTTP 客户端自行 再次解析 DNS,否则攻击者可以在校验时返回公网地址,在建连时返回私网地址。
也可以让策略化出站代理统一负责解析、分类和连接绑定。工作进程把规范 URL 与固定请求策略交给该代理, 不能接受攻击者指定的代理地址。做授权决策与打开套接字的必须是同一组件,或者通过严格受控边界传递 不可篡改的已批准地址结果。
DNS 正常情况下也会轮换地址,所以绑定只覆盖当前一跳,不应永久缓存。后续任务、重定向或显式重试都 重新解析和授权。连接池要按已授权 origin 与策略分组,不能因为两个不可信 URL 字符串相似就复用连接。
第四步:把每次重定向都当成新的出站请求
关闭 HTTP 客户端的自动重定向。收到重定向响应后,使用同一个解析器把 Location 与当前 URL 合并, 增加跳数,再重新执行协议、端口、userinfo、DNS、地址和连接检查。缺失或畸形的目标、循环、第 4 跳 以及任何落入禁止地址的跳转都拒绝。
第一跳不得转发调用方 Cookie、授权头或任意请求头,跨 origin 也不能携带响应建立的凭据。抓取器只 使用固定 User-Agent 和少量固定请求头。本题只提取标题,GET 足够;重定向不能把操作变成调用方可控 请求体的 POST。
这能封住常见绕过:攻击者控制的公网 URL 返回到 http://169.254.169.254/、 http://127.0.0.1/ 或内网服务的重定向。只批准第一条 URL,最终访问的就是未经授权的另一个目标。
第五步:让网络层拦住应用层遗漏
抓取工作进程运行在专用网络段或命名空间。出站防火墙只允许向受控解析器发 DNS,并且只允许 HTTP/ HTTPS 通过策略代理或批准的公网路径。对 IPv4 与 IPv6 都阻止回环逃逸、内部服务段、集群网络、公司 内网、链路本地和云元数据地址。验证实际生效的路由,不能只检查规则文本。
工作进程的服务身份只允许读取和完成预览任务,环境中不放宽泛云权限。在 EC2 上,不使用实例元数据时 可关闭该服务,或要求 IMDSv2。AWS 文档同时列出了 IPv4 元数据端点 169.254.169.254 和可选 IPv6 端点。这些措施提供纵深防御,不能替代 URL 目的地址策略。
预览任务应使用独立工作池,不能复用可访问内部 webhook 或管理接口的通用 HTTP 客户端。网络拒绝的 访问,包括元数据目标,要产生指标与告警,但对调用方不暴露内部拓扑。
第六步:限制抓取成本,只解析承诺返回的内容
分别设置 DNS、连接、首字节、空闲读取和 3 秒总时限。流式读取响应,解压后达到 2 MiB 就停止;只限 制压缩包大小仍可能遭受解压炸弹。只接受提取 HTML 标题所需的内容类型,并按账户和全局限制并发, 避免大量慢服务器占满工作进程。
默认不自动重试,因为重试就是另一次出站授权,也会放大负载。以后若产品需要重试,每次尝试都必须从 URL 解析和 DNS 解析开始,并且仍受整个任务预算约束。
解析器把响应体视为恶意输入。它不执行 JavaScript,不解析 XML 外部实体,不加载图片、样式表、字体、 iframe 或脚本,也不跟随页面中的元刷新。用有资源上限的流式或隔离解析器提取标题,把它规范为文本并 限制长度,只返回这个值。API 不返回原始 HTML,最终展示标题时还要在输出位置正确转义。
第七步:观测决策,并验证套接字级不变量
记录任务结果、规范主机或保护隐私的主机键、协议、有效端口、选中地址类别、重定向数、字节数、耗时 和稳定拒绝原因。完整 URL、查询字符串、userinfo、响应体和 DNS 载荷可能含秘密,不应进入日志。分别 统计私网、链路本地、元数据、协议、端口、重定向、大小、超时和内容类型拒绝率。
测试必须经过真实解析器、代理、防火墙和 HTTP 客户端,包括:
127.1、IPv6::1、IPv4-mapped IPv6 等回环变体;- 私网、共享、链路本地、组播、保留、文档和元数据地址;
- 同时返回一个公网和一个私网地址的域名,以及同时存在 A、AAAA 的情况;
- 校验时返回公网、下一次查询改为私网的解析器;
- 跳转到各种禁止地址的公网端点,以及产生 4 次循环跳转的端点;
- 内嵌凭据、编码主机、非 HTTP 协议、禁止端口和畸形 URL;
- 慢响应头、停滞响应体、解压后超限内容和解压炸弹;
- 脚本或图片指向内网主机的 HTML 页面;
- 套接字绑定指定 IP、TLS 仍验证原域名的合法公网 HTTPS 页面;
- 故意绕过应用策略,但仍必须被出站防火墙阻止的请求。
如果客户端二次解析 DNS,rebinding 测试就必须失败;如果证书误按数字 IP 校验,正向 TLS 测试也必须 失败。两项一起证明校验和连接使用的是预期身份。
高质量示范回答
“我会把 SSRF 防护定义为由建连组件执行的出站授权。API 只负责认证和限流,把 URL 作为不可信输入 保存并投递任务。工作进程使用标准解析器,拒绝 userinfo、非 HTTP 协议以及 80、443 之外的端口, 后续只使用解析器给出的规范主机和路径。
IP 字面量先规范化,IPv4-mapped IPv6 先还原,再通过持续维护的公网地址分类器。域名由受控解析器查询 全部 A 和 AAAA 结果;其中任一结果属于私网、回环、共享、链路本地、组播、保留、文档、元数据或 策略外范围,就拒绝整个目标。随后直接连接一个已批准 IP,同时保留原域名用于 Host、TLS SNI 和证书 校验,消除 DNS rebinding 利用的二次解析窗口。
关闭自动重定向和重试。最多 3 跳,每一跳都重新解析目标并完成整套授权后再建连。只发送固定请求头的 GET,不携带调用方 Cookie 或凭据。专用出站代理或防火墙再独立禁止 IPv4、IPv6 的内网、集群、公司 网络、链路本地和元数据路由;工作进程只有最小权限,也不能使用通用内网 HTTP 客户端。
每项任务总时限 3 秒,解压后响应上限 2 MiB。解析器不执行脚本、不加载子资源,只返回限制长度并清理 后的标题。日志记录原因码、地址类别、重定向数、字节和延迟,不记录完整 URL。验收覆盖混合 DNS 结果、 校验与建连间 rebinding、IPv4-mapped IPv6、跳转元数据地址、解压炸弹、慢响应体、绑定 IP 后的 TLS, 以及必须被网络层兜底阻止的应用策略绕过。”
常见错误
- 只拦截
localhost和 RFC 1918 → 特殊 IPv4、IPv6、链路本地、共享、保留和元数据地址仍可达
→ 规范化两类地址,并使用持续维护的公网目的地址策略分类。
- 用正则校验 URL → 校验器和 HTTP 解析器可能对凭据、编码、主机和端口产生不同理解 → **统一使用
标准解析器,并且只消费它的规范字段。**
- 解析并校验后让客户端再次解析 → DNS rebinding 能在批准后改变套接字目标 → **把连接绑定到已
批准 IP,同时用原域名完成 TLS 校验。**
- 只批准第一条 URL → 合法公网主机可以跳转内网 → 关闭自动重定向,逐跳重新授权。
- 从混合 DNS 结果中挑一个安全地址 → 地址选择变化后可能访问危险结果 → **本题只要有一个返回
地址违规,就拒绝整个域名。**
- 转发调用方请求头 → Cookie 或授权值会泄漏给攻击者指定的主机 → **构造不携带环境凭据的固定
出站请求。**
- 只依赖 IMDSv2 或云配置 → 其他内部服务与地址段仍然暴露 → **组合应用授权、最小权限、元数据
加固和出站阻断。**
- 只限制压缩前字节 → 很小的响应也可能解压到耗尽内存或 CPU → **同时限制解压后字节、解析工作、
时间和并发。**
- 像浏览器一样解析页面 → 脚本和子资源会发起未经审查的新请求 → **只提取承诺的内容,并关闭活动
内容与外部实体。**
- 为了排障记录完整 URL → 查询秘密和凭据进入可观测系统 → 只记录受限的规范字段和稳定原因码。
追问
所有目标都是固定合作方时,设计有什么变化?
使用经过评审配置的精确协议、规范域名和端口白名单。即使域名可信,也要解析和分类地址,避免 DNS 记录被攻破或配置错误后指向内网。确实需要访问合作方私有端点时,应建立单独的认证集成和明确网络路径, 不能塞进公网预览工作进程。
能否安全支持跨域重定向?
如果产品需要,可以,但每一跳都必须重复完整策略。去掉凭据和 origin 状态,解析新域名,检查全部地址, 绑定新连接并累计跳数。更简单的策略是只允许同 origin 跳转,但会挡住常见公网跳转服务;回答时应明确 这个产品取舍。
为什么一个 DNS 结果是公网、另一个是私网时要全部拒绝?
这样能形成稳定的失败关闭契约。不同解析器、IP 协议族和重试可能改变地址顺序;校验组件挑公网地址, 后续组件却挑到私网地址,就会重新打开漏洞。若产品一定要部分接受,必须保证同一组件在每次连接中只 绑定批准地址,并仔细测试故障切换;本题选择更简单保守的策略。
产品需要截图或执行 JavaScript 的预览怎么办?
把渲染放到隔离程度更高的独立层,并沿用同一出站策略。浏览器会产生大量次级请求,所以每次导航、 重定向、worker、WebSocket 和子资源都要拦截授权。关闭本地文件访问和无关浏览器能力,限制 CPU、 内存、时间与下载量,每个任务结束后销毁沙箱。初始页面 URL 不能自动给后续请求授予信任。
部署后如何证明防火墙真的生效?
从真实工作进程身份和网络命名空间运行受控探针,访问应被阻止的 IPv4、IPv6、集群、公司内网和元数据 目标,确认连接失败且网络遥测出现预期记录。路由、容器、代理或云网络变化后重复执行。配置审查只能 证明意图,探针和流日志才能证明实际路径。