内容提要
Filippo Valsorda提出Passkey凭证存储新规范,借鉴密码哈希字符串格式,将凭证压缩为不透明字符串,并起草crypto/passkey Go API草案,可能进入Go 1.28。该方案统一存储格式,简化服务端实现,并指出无需跨账号检查Credential ID唯一性,避免索引碰撞攻击。
延伸解读
存储格式统一的价值
Filippo Valsorda提出的passkey-record规范借鉴了密码哈希字符串(PHC)的语法,将WebAuthn凭证压缩为一行不透明字符串。这种格式复用了WebAuthn中已有的authenticator data CBOR编码,仅需额外附加transports参数。统一存储格式后,应用层只需维护用户账号与passkey record的关联,更换库或后端语言时无需迁移数据,降低了服务端实现的复杂度。
反直觉的安全洞见
文章指出,WebAuthn规范建议服务端检查Credential ID的跨账号唯一性,以防止索引碰撞攻击。但Filippo认为,这种攻击的前提是存在Credential ID索引。如果服务端不建立该索引,而是通过用户ID查找凭证,那么即使不同账号共享相同的Credential ID,也不会影响安全性。这类似于两个用户使用相同密码不影响系统安全。因此,设计时应避免让攻击者决定主键。
Go API 草案的实用性
crypto/passkey API草案设计了注册和登录的完整流程,应用层只需负责三件事:关联用户ID、存储passkey record、缓存挑战值。该API返回的JSON可直接与浏览器原生方法交互,无需手动转换。虽然尚未实现,但接口设计完整,为Go开发者提供了参考方向。草案还支持可发现凭证、二次验证、模态和条件UI等场景,覆盖了常见的Passkey使用需求。
Q&A
Filippo Valsorda 提出的 Passkey 存储新格式是什么?
他提出了 c2sp.org/passkey-record 规范草案,借用密码哈希字符串(PHC)的语法,将 Passkey 凭证压缩成一行不透明字符串,例如 $webauthn$v=1$transports=hybrid+internal$。该格式复用了 WebAuthn 中的 authenticator data CBOR 编码,并额外附加 transports 参数。
为什么现有的 WebAuthn 凭证存储方案需要统一标准?
因为不同应用、不同语言、不同库之间的数据库 schema 互不兼容,例如 Google 和 Adam Langley 给出的建表建议各不相同,导致应用间数据迁移困难。统一标准可以简化服务端实现,并支持跨库、跨语言的数据互操作。
crypto/passkey Go API 草案的注册流程是怎样的?
注册流程包括:1) 调用 RelyingParty.NewRegistration 传入用户信息和已有 passkey record;2) 将返回的 JSON 传给浏览器的 parseCreationOptionsFromJSON() 和 navigator.credentials.create();3) 将浏览器返回的 JSON 编码 PublicKeyCredential 传给 RelyingParty.Register;4) 将返回的 passkey record 存入数据库。
crypto/passkey Go API 草案的登录流程是怎样的?
登录流程包括:1) 调用 RelyingParty.NewLogin 生成登录请求;2) 将 request 以 RequestID 为 key 存入短 TTL 缓存,并将 JSON 传给 parseRequestOptionsFromJSON() 和 navigator.credentials.get();3) 将浏览器返回的 JSON 传给 Inspect,用 requestID 从缓存取回 request,用 userID 从数据库取回 passkey record;4) 将 JSON、request 和 passkey record 传给 RelyingParty.Login。
为什么不需要对 Credential ID 做跨账号唯一性检查?
因为 Credential ID 碰撞攻击依赖于服务端存在 Credential ID 索引。如果服务端不建立该索引,而是用用户 ID 查找凭证,攻击者就无法通过碰撞来影响其他账号。这就像两个用户使用相同密码不影响系统安全一样。
passkey record 中哪些元数据需要单独存储?
除了 passkey record 本身,应用可能想存储昵称、创建时间等元数据,这些不需要特殊处理。唯一例外是 backed_up 状态标志,因为它每次登录都可能变化,而 passkey record 不可变,所以必须单独存储并在每次登录时更新。
crypto/passkey API 草案目前的状态如何?
该 API 尚未实现,Filippo Valsorda 希望先收集社区对 passkey record 格式和 Go API 设计的反馈,之后再考虑正式提案,争取进入 Go 1.28。