三密钥模型
Connect 兑换需要由不同参与方持有的三个会话密钥。每一次经过 Connect 的身份认证往返都会在不同时刻签发三把彼此独立的密钥,分别由不同的当事方持有,防御不同类型的攻击。一次成功的 /redeem 必须同时出示这三把密钥。
本页讲的不是 Sudomimus 用来签令牌的长期密钥(那部分见 令牌与验证)。本页讲的是一次浏览器登录如何证明自己的合法性。
| 密钥 | 创建者 | 持有方 | 可见范围 |
|---|---|---|---|
| exposureKey | /establish |
应用后端 → 浏览器 | 浏览器、URL、via.sudomimus.com |
| hiddenKey | /establish |
仅应用后端 | 后端 |
| confirmationKey | 用户完成认证后由 via.sudomimus.com 签发 |
应用后端(通过回调) | 浏览器、URL |
exposureKey 和 hiddenKey 是同一次 /establish 调用成对签发的。它们属于唯一的一个会话,也只属于那个会话。
每把密钥都带有角色专属的前缀,便于在请求边界直接拒绝错位的密钥:
exp_+ 32 位小写 hexhid_+ 32 位小写 hexcnf_+ 32 位小写 hex
为什么是三把而不是一把
Section titled “为什么是三把而不是一把”如果仅凭一个不透明的会话引用就能换取令牌,那么任何得到该引用的攻击者都可以冒充用户完成兑换。兑换要求攻击者从各持有方取得全部三个密钥:
- 没有
hiddenKey—— 即使攻击者窃取了用户正在访问的 URL,也无法兑换令牌。hiddenKey永远不会离开应用服务器。 - 没有
exposureKey—— 即使攻击者攻破了应用服务器,也无法兑换其他用户的会话,因为每个待处理会话的exposureKey只会发送给对应的浏览器。 - 没有
confirmationKey—— 任何一方都无法兑换用户尚未完成的会话。只有用户在via.sudomimus.com上通过通行密钥或验证码挑战后,平台才会签发confirmationKey。
flowchart TD
Establish["POST /establish"] --> Exposure["exposureKey<br/>公开会话引用"]
Establish --> Hidden["hiddenKey<br/>服务端密钥"]
Exposure -->|重定向 URL| Browser["用户浏览器"]
Hidden -->|由其保管| Backend["应用后端"]
Browser -->|exposureKey| Via["via.sudomimus.com<br/>用户完成认证"]
Via -->|认证成功| Confirmation["confirmationKey<br/>认证证明"]
Exposure -. 随浏览器传递 .-> Callback["应用 callback"]
Confirmation -->|callback URL| Callback
Callback -->|exposureKey + confirmationKey| Redeem["POST /redeem"]
Backend -->|hiddenKey| Redeem
Redeem --> Tokens["accessToken + refreshToken"]
/redeem 成功之后,这三把密钥都会被消费掉,不能再次使用。即使应用重复发送同一组三元组,第二次也会失败。
与 OAuth 2.0 的对照
Section titled “与 OAuth 2.0 的对照”如果你熟悉 OAuth 的 authorization-code 流程,大致的对应关系是:
| Sudomimus | OAuth 2.0 |
|---|---|
exposureKey |
state 参数(大致对应) |
confirmationKey |
authorization code |
hiddenKey |
没有直接对应物 |
OAuth 使用长期存在的 client_secret 验证令牌交换请求。Sudomimus 则在每次 /establish 时生成新的 hiddenKey。某次会话的 hiddenKey 泄露只会影响这一次兑换,不会影响应用过去或未来的其他兑换。
这与「持有证明(proof-of-possession)令牌」的思路一致:短期、范围明确的密钥优于长期、被广泛共享的密钥。
如果需要标准的 OAuth/OIDC 语义,可以使用部署在 oidc.sudomimus.com 的标准 OIDC 提供方。三密钥模型用于底层 Connect 协议,通过 OIDC 接入的应用不会直接接触它。
除了上述三把会话密钥,每个应用还有两对长期 RSA-2048 密钥对:
- Token-signing 密钥对 —— Sudomimus 用私钥半边签 access / refresh JWT;应用通过 Session
GET /applications/{applicationAnchor}/jwks.json拿到公钥半边验证签名。 - Client-auth 密钥对 —— 应用用私钥半边(在应用创建和每次轮换时一次性下发)签每一次
/establish请求;Sudomimus 用存储的公钥半边校验。
这两对都不属于「三密钥」模型。它们认证的是 Sudomimus 与应用之间的通道;三把会话密钥认证的是一次登录。完整细节见 令牌与验证。