原文:https://blog.trailofbits.com/2025/05/14/the-cryptography-behind-passkeys/
作者:Joop van de Pol
译者:Kurt Pan
大多数人想到密码学时,通常首先想到的是加密:确保信息的机密性。但同样重要(甚至更重要)的是真实性:确保信息确实来自可靠来源。当你访问网站时,服务器通常会通过由 Web 公钥基础设施 (PKI) 认证的传输层安全性 (TLS) 证书来证明其身份。口令是传统的用户身份验证解决方案,但它容易受到网络钓鱼攻击和数据泄露的威胁。这时,通行密钥就派上用场了。
本文不会解释通行密钥是什么以及为什么它比口令更好 ( 很多其他资源已经讨论过了),而是会探讨通行密钥背后的密码学技术、它们能提供或不能提供的保证,以及你可以用它们实现的一些有趣的密码学功能,例如生成密钥和存储证书。你需要理解通行密钥背后的密码技术才能正确实现安全的身份认证。我们还将讨论主要的通行密钥规范 WebAuthn,并向你展示如何使用通行密钥机制的扩展来构建一个具有不同功能的更复杂的系统。
通行密钥本质上只是用于生成数字签名的密钥对。注册通行密钥时,网站会保存公钥和标识符。通过通行密钥验证用户身份时,网站会发出质询,并等待包含该质询(以及一些其他元数据,例如标识符)的签名响应。标识符用于查找公钥,而公钥则用于验证签名。
从密码学的角度来看,这非常简单。私钥用于验证用户身份,但不会将任何对攻击者有用的敏感信息传递给服务器。如果服务器质询能够正确生成(例如,生成一个 32 字节的均匀随机序列),则可以防止重放攻击。由于服务器只持有公钥,并且用户不会向其发送敏感信息,因此即使遭到黑客攻击,也不会泄露任何信息。
但仅靠数字签名不足以解决网络钓鱼问题。如果我们仅仅停留在密码原语层面,用户仍然容易受到攻击。例如,如果没有额外的安全措施,攻击者可能会诱骗用户为错误的网站签名,或在多个网站上重复使用相同的密钥对。
这就是为什么通行密钥是基于 W3C 的 WebAuthn 规范构建的,该规范在基本加密技术之外添加了关键的安全属性。让我们来看看 WebAuthn 如何将这些简单的密码原语转变为一个能够抵御网络钓鱼的身份认证系统。
WebAuthn 是通行密钥背后的主要规范。简单来说,用户通过笔记本电脑、手机或 PC(客户端设备)等设备上的浏览器 (WebAuthn 用户代理)访问网站 (依赖方)。浏览器与身份验证器 (生成密钥对的硬件或软件)交互,并使用此密钥对创建数字签名。

图 1:通行密钥认证流程的简化视图。
在上图中,你可以看到通行密钥认证的工作原理:
(浏览器和身份验证器之间的这种交互在另一个规范中进行了更详细的描述:FIDO 联盟的客户端到身份验证器协议 (CTAP)。)这是一个简化的描述;WebAuthn 规范允许更多种类的用例(例如,一切都可以通过移动应用程序而不是网站/浏览器进行)。然而,这些细节与理解通行密钥如何与密码学协同工作无关。
WebAuthn 通过来源绑定解决了网络钓鱼问题。该规范要求浏览器向身份验证器提供请求的来源 (即网站域名)。身份验证器仅当发出请求的网站与创建通行密钥的网站匹配时才会使用通行密钥。
这意味着,如果你为 bank.com 创建了密钥,fake-bank.com 这样的钓鱼网站根本无法使用它——你的身份验证器会拒绝该请求。每个网站还会获得各自独特的密钥对,从而彻底消除口令重复使用的问题。
此外,该规范仅允许使用 HTTPS 的来源,这意味着请求来自具有相应来源的有效证书的服务器。
一般来说,身份验证器就是“你拥有的东西”。所有身份验证器都可以在身份验证时检查用户是否真实存在。有些身份验证器还可以根据“用户已知的东西”(例如 PIN 码)或“用户本身的东西”(例如生物特征)来验证用户身份。
你会遇到两种主要类型的身份验证器:
如果某个平台支持跨平台通信(例如蓝牙),其平台认证器也可以通过与其他设备(例如智能手机 [1] )通信用作漫游认证器。为了在高价值应用中实现最高安全性,我们建议使用专用硬件安全密钥作为认证器。
某些身份验证器会显示正在生成数字签名的请求的用户详细信息。对于无法显示此信息的身份验证器,浏览器将显示这些详细信息。在批准身份验证请求之前,请务必验证这些详细信息。
当用户在网站上注册通行密钥时,身份验证器会生成一个通行密钥和一个标识符(凭证 ID)。网站会存储公钥和标识符,并将它们与用户帐户绑定。然后,网站可以使用此标识符告知身份验证器它们想要访问哪个密钥。一些身份验证器拥有大量存储空间,它们会自行存储所有用户密钥。其他身份验证器则没有,因此它们会加密通行密钥,并在注册期间将加密的通行密钥作为标识符提供给网站。当网站想要验证用户身份时,它会将标识符提供给浏览器,浏览器再将其提供给身份验证器,身份验证器解密并使用通行密钥。本质上,网站存储着通行密钥,但由于通行密钥是加密的,如果网站遭到黑客攻击,其价值就有限了。
理论上,你只需将密码学密钥对存储在文件中,并编写一些使用该密钥对进行加密操作的软件,即可将其伪装成身份验证器。但是,网站如何知道用户是否在使用安全的身份验证器呢?身份验证器可以通过在用户创建通行密钥时生成证明声明,以加密方式证明其来源的某些信息,例如制造商;该声明由制造商签名的证书链支持。这对于企业用户尤其有用,因为它允许企业确保所有用户都拥有符合某些安全要求的特定身份验证器。但是,证明是可选的:WebAuthn 规范并不要求身份验证器支持它。
最后,与任何“你拥有的东西”一样,一个重要的问题是,如果身份验证因素丢失或损坏会发生什么?一般来说,丢失身份验证器意味着丢失由其控制的所有通行密钥。由于通行密钥本质上是随机生成的密码密钥对,因此实际上没有恢复的希望。大多数平台身份验证器(例如 iCloud 钥匙串、Google 密码管理器和 1Password)都允许通过将通行密钥同步到云端来备份。然而,这始终是有代价的:可恢复的通行密钥具有更大的攻击面,因为攻击者可以尝试通过恢复机制获取通行密钥。一般来说,网站应该有一个恢复机制,以便在用户丢失通行密钥时使用,但同时也要记住,攻击者可能会将这种恢复机制作为攻击目标。
虽然使用具有备份功能的平台身份验证器可以降低丢失通行密钥的风险,但并不能完全消除。被平台封禁的用户将无法访问其通行密钥,平台也可能会意外删除这些通行密钥。此外,平台还可以支持通行密钥共享或家庭账户,允许多个用户访问相同的通行密钥。网站应根据通行密钥提供的访问权限,向用户发出这些风险的警示。
尽管你可能听说过各种营销宣传,但通行密钥并非万能的安全灵丹妙药。让我们来看看它们究竟能防御哪些攻击。
通行密钥的威胁模型表明,它们能够抵御口令通常能够抵御的威胁,同时还能消除网络钓鱼和口令重用的风险。这是一个显著的进步!WebAuthn 规范的 “一致性”部分做出了非常有力的声明,暗示符合该规范的网站、浏览器和身份验证器对于恶意行为而言是“安全的”。
这种说法过于简化了安全现实。以下是仍然可能发生的真实攻击场景:
attacker.com”,而实际上却向你的身份验证器发送了“google.com”的签名请求。通行密钥并不能完全防御大多数用户设备入侵行为,例如恶意浏览器或恶意软件。然而,它们可以有效地限制攻击速率,因为每个签名都需要用户与身份验证器进行单独交互。此外,通行密钥无法防御那些能够控制网站域名的攻击者,无论是通过直接接管还是通过子域名劫持。
网站需要考虑的另一件事是凭证 ID 冲突。规范仅要求凭证 ID 具有概率唯一性 ——这意味着它们是随机生成的,重复概率极低(但非零),类似于 UUID。
这为什么重要?当用户注册通行密钥时,网站会将凭证 ID 存储为该用户通行密钥的标识符。如果攻击者能够以某种方式注册与目标受害者具有相同凭证 ID 的密钥,他们可能会造成身份验证混乱。
这看起来可能有些牵强,但请考虑以下情况:
解决方法很简单:当新通行密钥的凭证 ID 与数据库中已有的凭证 ID 匹配时,网站应始终拒绝注册尝试 。这可以建立一个简单的“先到先得”机制,防止凭证 ID 冲突。
WebAuthn 还支持为用于生成凭据和执行身份验证的机制定义扩展。基本上,网站可以通过 WebAuthn API 请求使用一个或多个扩展。如果浏览器和身份验证器支持这些扩展,它们就会处理这些扩展,并忽略不支持的扩展。
WebAuthn 规范列出了一些已定义的扩展,并链接到互联网号码分配机构 (IANA) 注册中心以获取更多扩展的定义。这些扩展涵盖了从支持与旧 API 的向后兼容性到支持完全不同的密码功能等各种功能。由于本文主要讨论密码学,因此后者的扩展尤为引人注目。
WebAuthn 规范中有一个扩展,称为 prf (伪随机函数族),它基于 FIDO CTAP v2.1 规范中定义的 hmac-secret 扩展构建。使用 prf 扩展,认证器可以使用随机生成的固定 32 字节 HMAC 密钥计算 HMAC-SHA-256。HMAC 计算的输入是固定 WebAuthn 前缀的 SHA-256 摘要,后跟网站提供的输入。虽然此扩展不够灵活,无法实现类似 HKDF 的功能,但可以使用它来实现 HKDF Extract [2] 。
另一个类似的扩展名为 largeBlob ,它会提示支持身份验证器存储一个“大型 blob”不透明数据,网站可以在身份验证断言期间读取或写入这些数据。网站可以使用它来存储任何(敏感)数据,例如证书或密码学密钥 。
因此,使用这些扩展,可以派生或存储静态加密密钥。正如 largeBlob 示例所示,你甚至可以将其用于端到端加密。但是,与浏览器设置中的所有加密应用一样,实现真正的端到端安全性极其困难(甚至不可能)。通常,这要求系统能够抵御恶意服务器。Web 加密在服务器提供的 JavaScript 上运行,这意味着恶意服务器可以提供恶意 JavaScript,以提取密钥、将解密的消息发送回服务器等等。更糟糕的是,恶意服务器可以以高度针对性的方式执行此操作,向大多数用户提供正确的 JavaScript,但向特定目标用户提供恶意 JavaScript。在 Web 上实现代码的子资源完整性 (例如, 将所有已发布版本的哈希值存储在受信任的第三方 )和二进制透明技术(例如,可公开验证的防篡改日志)是解决此类问题的两个有希望的解决方案。
此外,值得注意的是,该规范将所有扩展视为可选扩展,这意味着无法保证浏览器和身份验证器都支持它们。网站在需要特定扩展时,需要检查扩展是否可用,否则用户将无法访问其服务。未来,所有主流浏览器和身份验证器有望都支持这些扩展,这将改善 Web 上密码学的密钥管理。
总体而言,该规范正在积极开发中,并且还有更多有趣的扩展空间。可能的扩展包括额外的密码学原语(例如更高级的签名方案和零知识证明),但单调计数器将是一个有趣的扩展。虽然这并非直接的密码学特性,但单调计数器可以用来保护外部存储(例如端到端加密云存储) 免受回滚攻击。
现在是采用通行密钥的时候了。通行密钥的密码学基础提供了强大的安全保障,如果与 WebAuthn 配合使用,它将成为现代身份验证系统的默认选择。虽然通行密钥并非完美的安全解决方案,但它消除了困扰口令数十年的许多关键漏洞:通行密钥永远不会将敏感信息传输到服务器,不能跨站点重复使用,并且可以通过来源绑定抵御网络钓鱼。
以下是我们给用户和开发者的建议:
用户应采用通行密钥,开发者也应尽可能支持通行密钥。硬件安全密钥为高价值应用提供最强的保护,而平台身份验证器通常提供更好的用户体验和备份功能。在不受信任的设备上进行身份验证时,请使用来自独立设备的通行密钥(该设备拥有独立的显示屏)来验证身份验证请求。
开发者应该实现账户恢复机制,因为通行密钥是密码学密钥对,一旦丢失无法重建。即使平台身份验证器具备备份功能,也存在用户应该了解的风险。
通行密钥可以作为第一身份验证因素、 第二身份验证因素 ,甚至是多重身份验证因素 。然而,开发人员需要将通行密钥纳入更广泛的威胁模型中来考虑。为了防范恶意服务器(例如在端到端(E2EE)应用程序中),请实现子资源完整性和二进制透明性技术。随着 WebAuthn 的发展,新的扩展将支持更多密码学应用程序,尽管不同浏览器和身份验证器对通行密钥的支持有所不同。
salt 参数是凭证随机生成的 32 字节密钥,输入密钥材料是 SHA-256 摘要。结果值可用作 HKDF Expand 的伪随机密钥。不建议以这种方式为每个密钥生成多个伪随机密钥。相反,可以通过改变 HKDF Expand 的 info 参数,从单个伪随机密钥派生出多个密钥。Kurt Pan: 即日起提供有偿「密码学论文代码实现和 benchmarking 服务」,语言侧重Rust / Python / C++,密码学侧重零知识证明系统和格密码方案。欢迎有需要的老师同学以及对密码学感兴趣的朋友联系我,邮箱kurtpan666 at pm dot me 或微信 cryptokurt(此號已因加過多好友被封,且無法解禁,抱歉),也可关注公众号后留言。