WebAuthn 服务器设置中的错误配置可能会导致用户体验问题并破坏现有的 Passkey。本指南旨在帮助开发者正确设置 WebAuthn 服务器。
Vincent
Created: August 20, 2025
Updated: August 21, 2025
Passkeys Series: WebAuthn Basics
See the original blog version in English here.
Our mission is to make the Internet a safer place, and the new login standard passkeys provides a superior solution to achieve that. That's why we want to help you understand passkeys and its characteristics better.
Passkeys 和底层的 WebAuthn 协议正在彻底改变身份验证领域。然而,正确设置 WebAuthn 服务器可能是一项棘手的工作。错误的配置不仅会引入漏洞,而且如果您以后需要更改它们,还可能会破坏现有的 passkey。这篇博文将帮助您更好地理解通常很复杂的 WebAuthn 规范,并为您提供针对您的用例的最佳设置建议。
WebAuthn 与三个主要实体一起运作:
下面描述了身份验证过程(登录或注册)中的高级数据流。
Ben Gould
Head of Engineering
I’ve built hundreds of integrations in my time, including quite a few with identity providers and I’ve never been so impressed with a developer experience as I have been with Corbado.
10,000+ devs trust Corbado & make the Internet safer with passkeys. Got questions? We’ve written 150+ blog posts on passkeys.
Join Passkeys Community上述高级流程描述了 WebAuthn 的注册和登录过程。Passkeys 是建立在 WebAuthn 之上的。最初,passkeys 这个术语被用作一个比 WebAuthn 更易记、更非技术的术语,描述的是同一件事。此外,与标准的 WebAuthn 相比,passkeys 提供了更多功能,例如通过云账户或密码管理器同步 passkeys 的可能性。
为了更好地理解以下部分,我们定义一些 WebAuthn 协议中的重要术语,以达成共识。
Resident key (RKs) 和 non-resident key (NRKs) 是 WebAuthn 协议中使用的两种加密密钥,它们主要在存储位置和检索机制上有所不同。
定义:
Resident key 直接存储在验证器本身上。这可能是一个安全密钥(例如,YubiKey)、平台的安全区域(例如,iPhone 的安全区域)或笔记本电脑上的可信平台模块 (TPM)。Conditional UI (passkey 自动填充) 只对 resident key 有效,并且 WebAuthn 工作组目前要求将 resident key 视为 passkey。
Resident key 通常也被称为可发现凭证 (discoverable credentials),因为客户端可以从验证器中发现与所讨论的信赖方 ID (rpID) 匹配的可能密钥列表,并显示设备上可能/已存储的用户句柄(例如电子邮件、电话号码、用户名)列表。
在下面的截图中,您可以看到存储在 YubiKey 上的所有 resident key(凭证 ID、rpID、用户名、显示名称)的列表。Non-resident key 不在此列表中,因为它们不存储在验证器上:
登录流程:
在登录过程中,信赖方发送一个不指定凭证的请求给客户端(浏览器),客户端开始查询验证器(图表中的安全密钥)。验证器发现所有与相应信赖方匹配的 resident key,客户端(浏览器)选择所需的 resident key。这个 resident key 用于对质询进行签名。签名从验证器通过客户端发送到信赖方。
优点:
缺点:
用例: 适用于用户经常进行身份验证的设备,如个人智能手机或笔记本电脑。
定义:
相比之下,non-resident key 不存储在验证器上。而是在注册期间,验证器生成一个新的密钥对(基于其内部保护的主密钥),并将这个新密钥对的公钥连同凭证 ID(包含一个种子)一起发送到服务器(信赖方)。然后服务器将此公钥与用户帐户关联。对于后续的身份验证,验证器通过接收凭证 ID,提取种子并将其与主密钥组合来重新派生私钥。因为该密钥仅在验证器的受保护内存中临时可用,所以在密码学术语中有时被称为临时密钥 (ephemeral key)。
Non-resident key 也常被称为不可发现凭证 (non-discoverable credentials),因为验证器无法为特定的信赖方 ID (rpID) 发现/搜索密钥。没有凭证 ID,验证器甚至不知道可能存在密钥。
登录流程:
在登录过程中,信赖方必须首先识别哪个用户正在请求身份验证(例如通过询问用户句柄,如电子邮件、电话号码或用户名),然后将其知道的请求用户的凭证 ID 发送到客户端(浏览器)。客户端将它们转发到验证器(此处为安全密钥)。验证器使用凭证 ID 和验证器的主密钥来派生临时私钥,用生成的密钥对质询进行签名,并将其返回给客户端(此处为浏览器),客户端再将其转发给信赖方。Non-resident key 最初在 passkey 引入之前用作第二因素。因此,首先识别用户是常规登录过程的一部分。
优点:
缺点:
用例: 适用于跨多个服务或平台使用的漫游验证器,如安全密钥(例如 YubiKeys)。
想象一下,您有一个安全密钥(例如 YubiKey),您用它注册了两个在线服务:服务 A 和服务 B。对于服务 A,您使用 resident key。对于服务 B,您使用 non-resident key。当您向服务 A 进行身份验证时,您只需轻触您的安全密钥(例如 YubiKey),就登录了——无需指定用户名。对于服务 B,您首先需要在浏览器/原生应用中提供您的用户名。然后,该服务将关联的凭证 ID 发送到您的安全密钥(例如 YubiKey),后者会重新生成用于身份验证的临时私钥。
从本质上讲,虽然 resident key 和 non-resident key 都通过利用加密身份验证来增强安全性,但它们的用户体验和存储机制不同,以适应不同的用例。
Conditional UI 是 passkeys / WebAuthn 的一个里程碑式功能。它为用户减轻了更多负担,因为用户不仅不需要记住密码,甚至不需要记住他们注册信赖方时使用的用户句柄(例如电子邮件、电话号码、用户名)。特别是在信赖方服务不经常使用的情况下,这是一个巨大的进步。
这是因为一旦登录页面打开,信赖方服务器就会在后台向客户端发送一个质询(请记住,在此调用中不需要发送凭证 ID)。客户端接收此质询并检查此验证器上哪些 passkey 与信赖方 ID 匹配,并通过自动填充菜单提供选择,用户可以在其中选择适当的 passkey。
此外,它还为用户省去了额外点击登录按钮的麻烦,因为一旦从自动填充菜单中选择了 passkey,登录过程就开始了。
Conditional UI (passkeys 自动填充) 仅适用于 resident key。
CTAP 是 FIDO2 标准中的一个基础协议,弥合了客户端(如浏览器)和验证器(如安全密钥,例如 YubiKeys 或智能手机)之间的通信鸿沟。为了更好地理解 CTAP,让我们在分析其对 resident key 和 non-resident key 的影响之前,先简要了解一下不同的协议版本。
早期版本,通常称为通用第二因素 (Universal 2nd Factor, U2F),主要设计用于第二因素身份验证。在 U2F 中,服务器提供一个质询,验证器提供一个响应,证明拥有私钥。然而,U2F 不支持 resident key,这意味着它总是需要服务器端查找来确定为用户使用哪个密钥,因为这是请求第二因素过程的一部分。
作为 U2F 的继任者,CTAP2 引入了对 resident key 的支持,从而实现了无密码和无用户名的身份验证。有了 resident key,验证器存储用户的用户名和凭证 ID(以及信赖方 ID),从而消除了信赖方服务器在身份验证期间提供它的需要。这是迈向更无缝用户体验的重大飞跃。
然而,CTAP2 也带来了挑战。一个显著的问题是 resident key 的管理。例如,在 CTAP2.0 设备中删除特定的 resident key 通常需要完全重置设备(因此您的主密钥也会被重置,这意味着您所有的 non-resident key 也将不再工作),这对用户不友好,并且在单个设备上存储多个 resident key 的情况下可能会出现问题。这使得 CTAP2.0 设备上的 resident key 成为一项严肃的承诺。您真的不想意外地填满您通常有限的空间,尤其是在安全密钥(例如 YubiKeys)上。
CTAP2.1 是 CTAP2 的后续版本,为协议带来了额外的功能和改进。关于 CTAP 2.1 的一些关键点包括:
在了解了 resident key、non-resident key 和不同的 CTAP 版本之后,我们现在更深入地分析信赖方侧,也就是 WebAuthn 服务器侧。
WebAuthn 的灵活性(也是其复杂性)在很大程度上归因于其服务器设置,特别是在 authenticatorSelection 对象中。该对象指导客户端为任务选择正确的验证器。
{ "rp": { "name": "corbado.com", "id": "corbado.com" }, "user": { "id": "dGVzdefEyMjE", "name": "test-username", "displayName": "test-username" }, "challenge": "mhanjsapJjCNaN_Ttasdfk1C0DymR-V_w_0UVOPsdfdfJG2ON_FK5-ODtqp33443tgqHzuuDjv-NUUmMAE4hg", "pubKeyCredParams": [ { "type": "public-key", "alg": -7 }, { "type": "public-key", "alg": -257 } ], "timeout": 60000, "excludeCredentials": [], "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "preferred", "requireResidentKey": false, "userVerification": "preferred" }, "attestation": "none", "extensions": { "credProps": true } }
此服务器选项指定验证器如何附加到客户端设备。它提供了有关验证器如何与客户端通信的见解:
可能的值
用例和示例
在 WebAuthn Level 1 规范中,此服务器选项称为 requireResidentKey,可以取布尔值 true(验证器必须创建 resident key)或 false(验证器必须创建 non-resident key)。WebAuthn Level 2 将此服务器选项替换为新的服务器选项 residentKey:
可能的值
用例和示例
用户验证指的是确保与验证器交互的人是合法所有者的过程,通常通过要求特定的身份验证手势,如输入 PIN、提供指纹或使用面部识别。
有时用户在场 (UP) 也被提及或与用户验证类似地使用,但确实有一些区别。用户在场仅确认有人——任何人——物理上在场并与设备交互。它不验证该人的身份。简单地触摸安全密钥,无需任何进一步的身份检查,就足以满足用户在场的要求。对于 passkeys / WebAuthn,用户总是需要在场。
可能的值
用例和示例
模式
示例: 一个企业应用程序希望员工在其公司配发的笔记本电脑上使用内置的指纹读取器进行无密码登录。凭证存储在设备上,用户每次都必须使用指纹进行验证。
模式
示例: 用户为其在线银行注册一个安全密钥(例如 YubiKey)。他们可以通过提供安全密钥(例如 YubiKey)在任何设备上使用此密钥进行身份验证,而无需输入用户名。
许多基于硬件的安全密钥(例如 YubiKeys)具有固有的存储限制。此限制是由于设备上的物理内存以及制造商的设计考虑。
示例: 一个 YubiKey 可能只能存储 20 个 resident key。一旦达到此限制,就无法在不删除现有密钥的情况下添加额外的 resident key。
解决方案:
不同的验证器可能以不同的方式处理 resident key 请求。有些可能默认创建 resident key,即使没有明确要求,而另一些则可能需要明确的指令。
不同WebAuthn 服务器选项在不同平台上的不一致行为是一个主要问题。例如,iOS 无论传递给它的 WebAuthn 服务器选项如何,总是会创建一个 resident key,而 Android 则需要选择加入(例如,使用 residentKey: preferred 或 residentKey: required)。
除了行为之外,更糟糕的是,根据服务器端存储的数据,您无法 100% 确定存储的凭证是 resident key 还是 non-resident key。相反,您可以遵循一些检查并缩小范围(见下文),但仍需希望凭证的行为与您的信念一致。
其背后的原因是 WebAuthn 建议在凭证属性扩展 (clientExtensionResults.credProps.rk,值为 true 或 false) 中存储有关凭证类型(resident key 或 non-resident key)的信息。然而,向 RP 提供此信息并不能保证,因为所有 WebAuthn 扩展都是可选的,例如 iOS 不发送它(所以您不知道它是 resident key 还是 non-resident key)。
在我们的研究中,我们尝试在不同平台和验证器(Windows 10、Windows 11、Android 7、Android 13、iOS17、macOS Ventura、YubiKey)上使用两个提供有关创建凭证更多详细信息的 WebAuthn 演示页面进行测试:https://webauthn.io 和 https://webauthntest.identitystandards.io/。后者提供了使用旧的 requireResidentKey 属性(WebAuthn Level 1)甚至将其与 residentKey 属性(WebAuthn Level 2)结合使用的可能性。然而,结果并不可靠(例如,它为 iOS 显示为 non-resident key,但 conditional UI 显然是有效的)。
我们发现的最值得信赖的检查方案如下:
如您所见,这有助于缩小范围,但仍然存在未知选项,您无法 100% 确定密钥类型。
这也与 SUSE 的 William Brown 在其精彩文章中的发现一致,该文章概述了如果信赖方要求使用 resident key,安全密钥(例如 YubiKeys)可能会变得无用(我们扩展了他的表格):
在下表中,您可以看到对于不同的 WebAuthn 服务器 resident key 选项,是否创建了 resident key(true)、是否创建了 non-resident key(false)或者发生了其他情况。
解决方案:
如果用户的安全密钥(例如 YubiKey)达到存储上限,他们可能会遇到错误或无法注册新凭证。这可能导致困惑和挫败感。
解决方案:
如上所述,您选择的 WebAuthn 服务器设置会显著影响用户体验和身份验证过程的安全性。了解这些设置的细微差别以做出明智的决定至关重要。
Resident Key 与 Non-resident Key: 如果您的大多数用户主要从个人设备(如智能手机或笔记本电脑)访问您的服务,resident key 是一个合适的选择。Resident key 存储在设备本身,为经常使用同一设备的用户提供无缝的身份验证体验。然而,对于使用安全密钥(例如 YubiKeys)的用户,non-resident key 可能更合适。
用户验证 (UV) 设置: 根据您希望达到的安全级别,您可以决定用户验证过程的严格程度。如果您的目标是高安全性,建议要求 PIN、指纹或面部识别(userVerification: preferred 或 userVerification: required)。
证明 (Attestation) 和可信度: 证明允许您验证验证器的来源和完整性。决定您是想信任所有验证器,还是只信任来自特定制造商的验证器。如果您处理敏感数据并希望确保只使用高质量、受信任的验证器,这一点可能至关重要。
后备机制: 始终准备好后备机制。如果用户丢失其验证器或验证器发生故障,他们应该有另一种方式访问其帐户。这可以通过备份代码、短信验证、电子邮件魔法链接或其他多因素身份验证方法实现。
持续学习: passkeys 和 WebAuthn 的领域在不断发展。随时了解最新的发展、漏洞和补丁。鼓励您的开发团队参加与 passkeys 和 WebAuthn 相关的研讨会、网络研讨会和会议。
用户引导和教育: 在引入passkey 身份验证时,确保您的用户了解其好处以及它如何工作。在注册过程中提供清晰的说明,并提供资源(如常见问题解答或视频教程)以帮助用户设置和使用 passkey。
通过遵守这些最佳实践,开发人员和产品经理可以确保他们有效地实施passkey 身份验证,平衡安全性和用户体验。
如果您想在您的网站或应用中为主流用户实施 passkey,并且不需要支持安全密钥(例如 YubiKeys),因为您的大多数用户只会使用他们的智能手机或笔记本电脑,我们推荐以下设置。
好处:
WebAuthn 的服务器设置虽然复杂,但为身份验证提供了一个强大而灵活的框架。掌握这些设置至关重要,因为它不仅仅是实施一个新标准;它关乎从根本上提高用户身份验证的安全性和效率。
从本质上讲,理解 WebAuthn 的服务器设置是对构建更安全、高效和具有前瞻性的应用程序的一项投资。随着数字领域的不断发展,精通此类技术将是不可或缺的。要保持更新,请加入我们的 passkeys 社区或订阅下面的新闻通讯。
Passkeys Series: WebAuthn Basics
Related Articles
Table of Contents