通用面试:如何设计 Oblivious HTTP 的 Relay 与 Gateway?
题干与适用场景
一个遥测客户端希望让目标服务无法把请求关联到客户端身份,同时又不让转发节点看到请求内容。请设计 OHTTP 的客户端、Relay、Gateway 和 Target,覆盖密钥发现、HPKE 封装、错误处理、重放防护、资源映射、限流和监控。
RFC 9458 将 OHTTP 定义为转发加密 HTTP 消息的标准:Relay 看到客户端连接,Gateway 能解密并访问 Target,但二者不应单独同时获得客户端身份与请求内容。它不是匿名网络;消息长度、时序、Relay/Gateway 串通、客户端元数据和应用层标识仍需单独处理。
面试官考察点
- 能否明确 Client、Relay、Gateway、Target 的可见数据和信任边界。
- 是否理解 Gateway 公钥、HPKE 封装请求/响应和媒体类型。
- 能否设计 replay 防护、请求大小限制、超时和错误映射。
- 是否考虑 Relay 与 Gateway 的一对一资源映射、流量分析和串通风险。
- 能否处理密钥轮换、缓存、失败重试和客户端时钟偏差。
- 能否用接受率、解密失败、重放拒绝、延迟和长度分布验证系统。
回答前需要澄清的问题
- 目标是单向遥测、公开读取,还是包含高价值写操作?敏感写操作需要更强的认证和防重放。
- Relay 与 Gateway 是否由不同运营方控制,是否允许同一组织拥有两者?
- Target 是否需要按用户授权、租户或地域返回不同结果?
- 请求大小、批量窗口、最大延迟和丢失容忍度是多少?
- 是否允许客户端在密钥过期时重新发现配置,日志中哪些字段必须脱敏?
30 秒回答框架
我会先画出四方边界:客户端向 Relay 建立连接,Relay 只转发封装消息,Gateway 用公开密钥解密并调用 Target。客户端从 Gateway 获取 key configuration,用 HPKE 封装二进制 HTTP 请求;响应沿相反路径封装返回。Gateway 用时间窗口、唯一请求标识或应用级幂等防重放,Relay 限制连接与消息资源。密钥轮换保留重叠期,监控解密失败、重放拒绝、延迟和长度分布;不把 OHTTP 当成能抵抗串通或流量分析的匿名系统。
分步骤深入解答
1. 先定义四方职责
Client 知道目标资源和 Gateway 公钥;Relay 知道客户端网络连接但不应读取封装内容;Gateway 解密请求并向 Target 发起普通 HTTP;Target 只看到 Gateway 的来源。Relay 和 Gateway 的部署、日志与访问权限应分离,否则单点可能同时关联身份与内容。
2. 发现并验证密钥配置
客户端获取 Gateway 的 key configuration,其中包含 key identifier、HPKE 算法和公钥。配置需要版本、有效期和来源认证;客户端拒绝不支持的算法或过期 key。轮换时同时发布旧、新 key,等待客户端缓存与请求窗口结束后再撤销旧私钥。
3. 封装请求和响应
客户端把二进制 HTTP 请求编码后用 HPKE 封装,发送 message/ohttp-req 负载;Gateway 解封装后校验方法、目标资源、大小和内容类型。响应再以 message/ohttp-res 封装给客户端。Relay 不应解析内层 HTTP,也不应根据明文状态码做差异化路由。
Client -- TLS --> Relay -- opaque OHTTP --> Gateway -- HTTP --> Target
Client <-- opaque response -- Relay <-- OHTTP response -- Gateway4. 处理认证、重放和幂等
OHTTP 隐藏客户端网络身份,不能自动提供业务用户认证。对写请求使用应用层签名、一次性 nonce、时间窗口或幂等键;Gateway 维护最小的重放检测状态,并限制同一封装消息重复提交。对遥测批次可采用幂等事件 ID,避免重试产生重复计数。
5. 处理错误和资源映射
Gateway 无法解密时返回结构化的 key 或封装错误,不能把内部密钥细节写入响应。Target 的业务错误要通过 OHTTP 响应安全传回。Relay 与 Gateway 的资源映射应固定且可验证,避免 Relay 把封装消息转发到错误目标。超时、大小超限和过载应在各层分别限流。
6. 评估隐私泄露和流量分析
即使内容加密,Relay 仍能观察连接时序、消息长度和客户端 IP;Gateway 仍能观察请求频率、解密后的应用字段和 Target。使用批量窗口、填充、速率限制和分离日志降低关联风险,但会增加延迟和成本。不要承诺 OHTTP 防止 Relay/Gateway 串通。
7. 灰度、监控和回退
先让一小组客户端使用独立 Relay/Gateway 资源,记录配置命中、解密成功率、重放拒绝、Target 5xx、p95 延迟、消息大小分布和回退率。故障时只对目标资源或客户端版本停用 OHTTP,普通 HTTPS 回退必须经过隐私评审;日志保存配置 ID、错误类别和请求哈希,不记录内层身份字段。
高质量示范回答
我会把客户端、Relay、Gateway 和 Target 分成四个责任域。客户端获取并验证 Gateway 的 HPKE key configuration,把二进制 HTTP 请求封装后发给 Relay;Relay 只转发不透明消息;Gateway 解封装、检查大小与资源映射,再以自己的身份请求 Target,响应沿相反方向封装。
OHTTP 不提供业务认证,也不抵抗 Relay 与 Gateway 串通。因此写请求要加应用级签名、nonce 或幂等键,Gateway 用时间窗口做重放拒绝。密钥轮换保留新旧重叠期,Relay 限制连接和消息资源。灰度看解密失败、重放拒绝、延迟、长度分布和业务错误;只在明确评估后才允许普通 HTTPS 回退。
常见错误
- 把 Relay 当作能解密请求的代理 → 失去隐私边界 → Relay 只处理外层连接和不透明消息。
- 认为 OHTTP 自动完成用户认证 → Target 无法识别合法业务主体 → 使用应用层签名或授权令牌。
- 忽略消息长度和时序 → 流量分析仍可关联 → 采用填充、批量窗口并衡量延迟成本。
- 重试写请求却没有幂等键 → 遥测重复计数或副作用重复 → 设计 nonce、事件 ID 和重放窗口。
- Relay 与 Gateway 共用可关联日志 → 单点即可还原身份与内容 → 分离运营、日志字段和访问权限。
追问及应对
OHTTP 能否防止 Relay 与 Gateway 串通?
不能。协议依赖有限信任和运营分离;串通方可能把连接身份与解密内容重新关联。需要组织、日志和访问控制层面的独立性。
Gateway 如何防重放?
对写请求使用时间窗口、nonce、幂等事件 ID 或应用签名;保存最小检测状态并拒绝重复封装消息。只依赖 TLS 连接不能防止跨连接重放。
为什么需要资源映射?
一个 Relay 应把封装请求转发到指定 Gateway/Target 资源。固定映射让密钥、策略、限流和审计边界可验证,也避免把消息误发到不兼容的 Gateway。
如何轮换 Gateway 公钥?
发布带版本和有效期的新 key configuration,保留旧 key 的重叠窗口,按 key ID 观察命中与解密失败,等缓存、批量和重试窗口结束后再撤销旧私钥。
OHTTP 适合高价值写操作吗?
需要谨慎。它隐藏网络身份但不替代认证、授权、幂等和审计;高价值写操作应先证明应用层主体和重放边界,再决定是否接受额外延迟与隐私收益。
监控应该记录什么?
记录配置 ID、Relay/Gateway 资源、错误类别、请求大小桶、重放拒绝和端到端延迟;默认不记录内层域名、用户标识或原始封装内容。