内容提要
前Go密码学负责人Filippo Valsorda提出在Go标准库新增crypto/passkey包,以无状态、无回调、无接口的极简设计简化中小型网站Passkey接入。该提案引入passkey-record规范,将凭据存储为不透明字符串,仅按用户ID索引,并摒弃回调机制、强制单一Origin配置,以降低安全风险。目前提案仍在评审中,但已释放简化Passkey接入的信号。
延伸解读
设计取舍:为95%场景做减法
提案明确放弃attestation和签名计数器校验,理由是主流Passkey提供商基本不维护计数器,强行校验反而会拒绝合法登录。这种务实取舍体现了为消费级Web应用降维减负的思路,但也意味着企业级高安全场景可能不适用,开发者需评估自身需求。
安全模型的关键:User ID与Origin
提案强调User ID必须为随机不透明字符串,避免泄露用户信息,并建议用crypto/rand.Text()生成。同时,强制单一Origin配置,防止跨站断言重放。开发者需注意多来源场景需创建多个RelyingParty实例,避免信任判断交给运行时。
存储模型简化:不透明字符串与索引
passkey-record将凭据编码为不透明字符串,应用只需按用户ID索引,无需对credential_id设唯一校验。这简化了数据库设计,但要求开发者理解其安全论证:凭据始终按用户ID查询,不存在跨账号碰撞风险。跨语言互操作是额外优势。
Q&A
Go标准库新增的crypto/passkey包是什么?
crypto/passkey是前Go密码学负责人Filippo Valsorda提出的一个提案,旨在为Go标准库添加一个无状态、无回调、无接口的Passkey服务端API,以简化中小型网站接入Passkey(免密登录)的流程。
crypto/passkey提案中的passkey record是什么?
passkey record是一种不透明字符串格式,将服务端需要保存的WebAuthn凭据信息编码为类似密码哈希的单行文本,形如$webauthn$v=1$transports=hybrid+internal。应用只需将其作为整体存储,无需关心内部结构,支持跨语言、跨数据库的互操作。
crypto/passkey提案为什么建议数据库只按user_id建立索引,而不对credential_id做唯一校验?
因为凭据总是先按用户ID查出该用户全部记录,再逐条比对,不存在跨账号撞上同一个凭据ID的风险,因此WebAuthn规范建议的跨账号唯一性检查不再需要,简化了存储模型。
crypto/passkey提案中为什么放弃回调机制?
因为回调机制可能导致未经验证的数据被传给回调,引发安全漏洞,如GO-2024-3321漏洞。提案改用ParseResponse解析出Response对象,由应用自行决定如何使用其中的UnauthenticatedUserID,将决策权和风险提示留在应用侧。
crypto/passkey提案中为什么强制单一Origin配置?
因为如果允许多个来源,不同来源的可信程度不同,低信任来源被XSS攻击后可能截获登录断言并用于高信任来源,造成冒充登录。强制单一Origin配置可以降低这种跨站断言重放的风险,需要多来源时可为每个来源创建独立的RelyingParty实例。
crypto/passkey提案为什么不支持attestation和签名计数器校验?
因为attestation在消费级Passkey生态中几乎没有实际使用价值,而主流Passkey提供商基本不维护签名计数器,强行校验反而会拒绝合法登录。提案旨在为95%的消费级Web应用做减法,而非追求协议层面的全面性。
crypto/passkey提案目前处于什么阶段?
该提案目前仍在评审中,走的是Go官方标准的提案评审流程,先在Issue中公开讨论、收集反馈,经Go团队评估决定是否采纳,采纳后才会进入开发计划并随正式版本发布。