代表性面试主题

后端面试:如何用 HTTP Message Signatures 保护 API 完整性?

后端困难
OfferCC 编辑团队发布 更新

题干

一个支付合作方要求验证每个 HTTP 请求的发送方和内容完整性。请基于 RFC 9421 设计签名与验签流程,并说明覆盖哪些组件、如何防重放、轮换密钥以及处理代理改写。

题干与适用场景

一个支付合作方要求验证每个 HTTP 请求的发送方和内容完整性。请基于 RFC 9421 设计签名与验签流程,并说明覆盖哪些组件、如何防重放、轮换密钥以及处理代理改写。

RFC 9421 将签名输入、覆盖的 HTTP 组件和 created、expires、nonce 等参数分开定义。题目考察能否把“谁签的”和“哪些字节被签”落实为可互操作的协议,而不是简单说“给 body 做 SHA-256”。

面试官考察点

重点包括:覆盖组件是否足以绑定请求语义;客户端和服务端如何规范化签名基线;时间、nonce 和密钥 ID 如何防重放;代理、重试、压缩和多租户如何影响验签;以及密钥发布、吊销和监控是否可运维。

30 秒回答框架

“我先定义协议要求覆盖的方法、目标路径、authority、关键业务头和 Content-Digest,并固定签名算法、参数顺序和时钟容差。服务端先解析 Signature-Input,按相同规则重建签名基线,再查密钥、验证签名、检查 created、expires 和一次性 nonce,最后才进入业务。密钥用版本化 key ID 支持双写验签和吊销;代理只能改写未覆盖字段,否则验签失败。所有失败原因做分类指标,但不把详细密钥信息返回给调用方。”

分步骤深入解答

第一步:明确要证明的属性

签名可以证明持钥方对特定 HTTP 组件做过签名,并帮助检测传输中的改写;它不替代 TLS、授权、输入校验或业务幂等。先写清楚需要认证请求、完整性,还是不可否认性。

第二步:选择覆盖组件

至少覆盖方法、目标路径和 authority;按业务再覆盖 content-digest、幂等键、租户 ID 或关键业务头。不要只签一个可被替换的业务字段,也不要盲目覆盖会被合法代理重写的头。组件清单必须成为版本化协议。

第三步:规范化签名基线

客户端和服务端必须使用 RFC 9421 规定的组件标识、顺序、参数编码和派生组件规则。服务端不能直接拼接原始 Header 字符串猜测结果;应从解析后的请求和 Signature-Input 重建同一基线,并拒绝未知或重复的关键组件。

第四步:保护请求内容

对有 body 的请求生成 Content-Digest,再把该字段纳入签名。验签前先按收到的字节计算摘要,确保压缩、转码或 JSON 重排不会静默改变语义。若代理会解压或重编码,协议必须明确签名发生在修改前还是修改后。

第五步:设计时间与防重放

要求 created,必要时要求 expires 和唯一 nonce。服务端校验时间窗口和允许的时钟偏差,并在租户范围内短期记录已用 nonce;重试相同业务请求应依赖幂等键,而不是无限放宽签名有效期。时间检查通过不代表业务请求可以重复执行。

第六步:实现密钥发现与轮换

Signature-Input 中携带 key ID,服务端从受控目录或 JWKS 类发布端点读取公钥,并缓存带版本的结果。轮换时先发布新公钥,再允许新旧 key 双验签,确认旧流量耗尽后吊销旧 key。私钥只在签名端使用,日志不得记录密钥材料或完整签名基线。

第七步:处理代理与重试边界

明确哪些层可以添加追踪头、重试请求或改变 authority。被覆盖组件发生任何不被协议允许的改写都应验签失败;代理重试时不能复制一次性 nonce,也不能把一个签名转发到不同目标。服务端在验签前不要执行写入或昂贵业务逻辑。

第八步:观测和故障处理

记录 key ID、签名版本、失败类别、时钟偏差、nonce 冲突、组件缺失和代理来源,使用请求 ID 关联日志。对合作方提供可操作的错误码,例如过期、未知 key、摘要不匹配;不要返回可帮助攻击者试错的详细密钥或基线内容。

设计取舍与边界

非对称签名还是共享密钥

非对称密钥便于多方验签和独立吊销,代价是签名与密钥基础设施更复杂。共享 HMAC 成本较低,但验证方也能伪造请求,适合双方完全互信且边界清晰的场景。

覆盖范围还是代理兼容

覆盖越多,完整性约束越强;覆盖会被网关合法修改的字段则会造成误失败。把代理契约写入协议,必要时由边界代理重新签名,而不是静默放宽验签。

严格时间窗口还是离线重试

短窗口降低重放风险,却要求时钟同步和快速重试。离线客户端应申请新的签名,不应复用长期有效签名;服务端可按合作方设置受控容差。

失败演练与演进计划

代理修改路径

在测试代理中改写路径或 authority,确认验签失败且没有进入业务;允许的追踪头改写不应影响签名。

重放旧请求

重复发送相同签名和 nonce,验证第二次被拒绝;使用新的 nonce 但相同幂等键时,验证业务层按幂等规则处理。

密钥轮换窗口

同时发布新旧 key,验证新旧签名均能按策略通过;撤销旧 key 后旧签名应失败,且不会因缓存无限存活。

常见误区与追问

误区一:只签 body 的哈希

追问:攻击者能否把签名复制到另一个路径或方法?应覆盖目标组件并绑定请求语义。

误区二:认为 HTTPS 已经足够

追问:终止 TLS 的代理之后如何证明原始发送方和内容没有被内部层改写?

误区三:忽略 nonce 和时间

追问:捕获的合法签名能否在有效期内重复扣款?应结合时间窗口、nonce 和幂等键。

误区四:轮换时立刻删除旧 key

追问:在途请求和多区域缓存如何过渡?应先双验签,再吊销旧 key。

误区五:把验签错误直接返回基线细节

追问:如何让合作方排障又不泄露签名输入、密钥或内部代理信息?

延伸追问与参考答案

Content-Digest 为什么还要放进签名?

摘要证明 body 字节,签名把摘要与方法、目标和其他组件绑定,避免攻击者把一个合法摘要搬到另一请求。

代理改写后应该重新签名吗?

如果改写字段属于下游协议的一部分,边界代理应以自己的身份重新签名;否则应保持被覆盖组件不变并让原签名继续验证。

如何区分认证和授权?

验签确认持钥方签过请求,授权仍需检查租户、账户、金额、幂等键和业务状态,不能由签名单独决定是否执行。

公开来源

同类题目

资源

产品对比

每份对比均基于官方资料,并标注复核日期。

查看完整对比