后端面试:如何结合 OAuth RAR 与 DPoP 保护交易授权?
题干与适用场景
一个移动银行客户端调用支付 API,用户批准向指定收款人转账 500 美元。授权服务器要在同意页面展示交易细节,复制出的访问令牌不能被另一个客户端实例直接使用。请设计 OAuth 2.0 Rich Authorization Requests(RAR)与 Demonstrating Proof of Possession(DPoP)的组合方案,覆盖令牌签发、资源服务器校验、重放、密钥丢失、账本状态和验证。
这是后端安全与 API 契约题。金额、币种和收款人是面试假设,不是市场事实。
面试官考察点
- 是否能区分用户同意的交易和访问令牌可调用的粗粒度权限。
- 是否知道 RAR 用结构化授权详情表达动作,DPoP 用密钥绑定令牌并为每次请求证明持钥。
- 是否能说清授权服务器、令牌端点、资源服务器和支付账本各自负责什么。
- 是否覆盖重放、重复提交、设备丢失、密钥泄露和支付方超时。
- 是否用业务不变量和灰度指标验证方案,而不是只检查 JWT 格式。
弱回答会说“把金额放进 scope,再签一个 JWT”。强回答会保留结构化交易字段、绑定客户端密钥,并让最终账本状态机决定是否扣款。
回答前需要澄清的问题
- 授权服务器是否拥有支付账本?若没有,账本必须在提交时再次检查授权决定。
- 一次授权只允许一笔转账,还是允许有限次数?这会改变授权详情和令牌寿命。
- 客户端能否用硬件保护密钥跨重装保存?丢失设备如何撤销?
- 资源服务器是否会要求 DPoP nonce?允许多大的时钟偏差?
- 支付方超时后是否能查询状态?否则必须保留未知状态,不能直接重试。
30 秒回答框架
“我会用 authorization_details 表达精确转账,而不是发明一个包含金额的 scope。客户端先创建密钥对,请求令牌时发送 DPoP proof,授权服务器把令牌绑定到公钥。资源服务器验证访问令牌、DPoP proof、方法、URI、时间、nonce 和密钥绑定,再把授权记录 ID 交给支付账本。账本用幂等键只允许一次终态转换。重放、设备撤销、支付方超时和审计证据分别由各自边界负责,并用测试证明业务不变量。”
分步骤深入解答
1. 建模被请求的动作
RAR 定义结构化的 authorization_details 请求。对象应包含注册过的 type、动作、金额、币种、收款人标识和交易请求号。授权服务器先校验 schema、账户归属、限额和策略,再展示同意页面。展示的规范化值必须与账本最终使用的值相同,不能只展示客户端传来的名称。
{
"type": "payment",
"actions": ["transfer"],
"amount": "500.00",
"currency": "USD",
"beneficiary_id": "b_123",
"request_id": "rq_7f2"
}授权结果应引用服务端拥有的授权记录。资源服务器不能从不可信的展示文本重新推导金额、币种或收款人。
2. 把令牌绑定到客户端密钥
客户端把私钥保存在受保护存储中,并向令牌端点发送 DPoP proof JWT。proof 包含 HTTP 方法、目标 URI、签发时间、唯一 proof ID 和公钥。授权服务器校验 proof,签发带密钥确认信息的令牌。攻击者即使复制访问令牌,没有私钥也无法完成受保护请求。
DPoP 是应用层的持有者约束,不能替代 TLS、安全密钥存储、授权策略或设备撤销。密钥证明的是客户端实例持有某把钥匙,不等于证明用户批准了某一笔付款。
3. 校验每一次资源请求
客户端同时发送访问令牌和新的 DPoP proof。资源服务器检查签名、令牌过期、issuer、audience、确认指纹、方法、URI、时钟窗口、必要的 nonce 和 proof ID 重放缓存。复用到另一 URI 或超过窗口的 proof 必须拒绝。访问令牌绑定和授权详情查询要一起校验;另一枚令牌的有效 proof 不能代替本次检查。
4. 让账本成为最终权威
资源服务器生成支付命令,带上授权记录 ID、规范化金额和收款人摘要、客户端实例 ID 与幂等键。账本在一个事务中把命令与批准记录比较,只接受未使用的授权,写入 PENDING 或 COMMITTED,拒绝金额或收款人变化、授权过期和第二次终态转换。外部支付方超时时写入 UNKNOWN,通过查询或对账确认后再处理;超时不等于没有扣款。
5. 处理恢复与撤销
按受保护时间窗口保存 proof ID,并按客户端和 issuer 限制缓存。丢失设备时撤销密钥绑定或授权记录,而不只清理浏览器会话。审计事件记录同意内容摘要、展示值、令牌密钥指纹、资源判定、账本转换和人工操作;私钥与原始令牌不得写入日志。新授权详情类型应能单独关闭,保留旧流程作为回退。
6. 比较替代方案
粗粒度 payments.write scope 简单,但无法表达一笔金额和一个收款人;账本仍需另一个可信交易授权。mTLS 也能把令牌绑定到证书,适合受控服务端客户端,但移动设备证书生命周期和网络终止更复杂。DPoP 适合应用管理密钥的 HTTP 客户端,代价是必须实现 nonce、时钟、重放缓存和密钥丢失处理。
高质量示范回答
“我会拆成四个决定。第一,RAR 携带包含金额、币种、收款人和服务端请求号的类型化支付授权,同意页面显示规范化值,授权服务器校验限额。第二,移动客户端使用受保护密钥和 DPoP proof,令牌绑定该密钥。第三,资源服务器验证令牌、proof、URI、时间、nonce 和 proof 重放,再读取服务端授权记录。最后,支付账本在事务中比较授权记录和命令,只允许一次幂等终态转换。没有私钥的复制令牌、变化的收款人、重复 proof 和第二次提交都会被拒绝。支付方超时进入 UNKNOWN 并对账,绝不盲目重试。我会监控重放拒绝、nonce 失败、授权不匹配、重复命令、未知结果和撤销传播,并用开关灰度新 RAR 类型。”
常见错误
- 把金额和收款人放进 scope → scope 变成无边界字符串,失去类型校验 → 用注册的授权详情 schema 和服务端记录。
- 把 DPoP 当成人类同意 → 持有密钥不代表用户批准了什么 → 分离同意、令牌绑定和账本授权。
- 只在网关校验 DPoP → 内部调用或另一条路由可能绕过 → 在每个资源边界校验,并传递可验证的授权记录。
- 支付方超时就创建新付款 → 第一次请求可能已经成功 → 查询状态、使用幂等键、保留
UNKNOWN。 - 永久保存 proof ID → 缓存无限增长且策略难以解释 → 按 proof 寿命和时钟策略设置有界重放窗口。
追问及应对
如果攻击者同时窃取访问令牌和 DPoP proof 呢?
拒绝复用 proof ID,并执行方法、URI、时间和 nonce 约束。proof 寿命要短,每次请求重新生成。如果私钥也泄露,撤销密钥绑定和授权记录;DPoP 本身无法修复已泄露的密钥。
为什么不把付款写入 JWT scope?
scope 适合粗粒度权限,付款却需要类型字段、展示、校验和稳定服务端记录。可以保留 scope 作为大能力边界,再由 RAR 与账本执行精确交易约束。
资源服务器和授权服务器意见不一致怎么办?
使用服务端拥有的授权 ID 和版本。高风险动作在记录不可用或版本过旧时应失败关闭。账本在事务中再次检查版本,旧缓存不能提交修改后的付款。
DPoP 能阻止所有重放吗?
不能。它在攻击者没有私钥时降低复制令牌的重放风险,并帮助服务端识别重复 proof。持有密钥的恶意客户端仍可能重复提交已获准命令,所以账本幂等、一次性授权、限流和设备撤销仍然必要。