题干与适用场景
你负责一个已有密码登录的网站,计划增加 Passkey。产品希望用户在支持的浏览器中使用系统解锁或安全密钥登录,同时保留可理解的降级和恢复路径。请设计前端与服务器的协作,说明 navigator.credentials.create()、navigator.credentials.get()、挑战值、RP ID、origin 校验和凭证管理边界。
WebAuthn 是浏览器与 authenticator 之间的公钥认证 API;私钥留在 authenticator,网站保存公钥。前端只负责获取服务器选项、调用浏览器 API、序列化结果和呈现状态,签名验证、挑战消费和账号绑定必须在服务器完成。
面试官考察点
强回答会把注册和登录拆成两个有状态的短流程:服务器生成一次性 challenge,前端调用 authenticator,服务器验证回传的 challenge、origin、RP ID、签名和计数器,再建立会话。还要覆盖 HTTPS 安全上下文、用户取消、超时、设备迁移、多个凭证和恢复,不把“有 Face ID 就成功”当成协议设计。
面试官会特别看你是否理解条件式自动填充只是 UX 能力,不能绕过服务器验证;以及为什么删除服务器凭证后,还要用 WebAuthn 的信号 API 让 authenticator 更新状态。
回答前需要澄清的问题
支持范围与账户策略
确认目标浏览器、移动端和桌面端,是否允许跨设备同步的 discoverable credential,是否仍保留密码或安全密钥。不同策略决定 residentKey、userVerification、凭证选择和帮助文案。
域名与部署拓扑
确认正式域名、登录子域、iframe、反向代理和多租户域名。RP ID 必须是当前 origin 的有效关联域名;如果环境从预览域切到正式域,不能用同一组配置想当然地复用凭证。
恢复与高风险操作
问清楚用户丢失所有 authenticator 时如何证明身份,以及重置后是否要撤销已有会话、通知用户和延迟高风险操作。恢复是身份生命周期的一部分,不能只在前端显示“改用密码”。
30 秒回答框架
“注册和登录都由服务器先生成一次性、不可预测的 challenge,前端只把 options 交给 WebAuthn API。注册时服务器验证回传的 challenge、origin、RP ID、attestation 策略和公钥,再把 credential ID 与账户绑定;登录时验证 assertion 的签名、challenge、RP ID、origin 和签名计数器,成功后才建立会话。前端明确区分不支持、用户取消、超时和服务器拒绝;支持条件式 mediation 时提供自动填充,但不依赖它。用户丢失设备时要求经过风控的恢复流程、撤销旧会话并允许注册新凭证。”
分步骤深入解答
第一步:让服务器生成挑战
注册或登录页面先请求服务器创建流程,服务器生成至少 16 字节的随机 challenge,保存哈希、账户、用途、过期时间和已消费状态。challenge 不能由前端生成或复用,否则攻击者可以把旧断言搬到另一流程。服务器返回符合当前 RP 的 publicKey options,前端不要自行修改安全相关字段。
第二步:设计注册流程
前端在 HTTPS 安全上下文中调用 navigator.credentials.create()。注册选项包含 RP、用户 ID、显示名、允许的公钥算法、用户验证偏好和 discoverable credential 策略。Promise 成功后,前端把 credential ID、client data、attestation response 和必要扩展序列化发回服务器;私钥不离开 authenticator。
第三步:服务器验证注册结果
服务器检查 challenge 与流程记录一致、origin 属于允许集合、RP ID hash 正确、签名和公钥算法符合策略,并根据隐私与兼容目标决定是否验证 attestation。成功后只保存公钥、credential ID、用户账户、签名计数器和设备标签等必要资料。不能把整个原始对象或可识别设备信息无限期保存。
第四步:设计登录与条件式自动填充
登录时服务器生成新的 challenge,前端调用 navigator.credentials.get() 并提交 assertion。服务器验证 challenge、origin、RP ID、签名和签名计数器,再根据 credential ID 找到账户。支持条件式 mediation 时,页面可以在用户聚焦用户名输入框后请求可发现凭证,让浏览器把 Passkey 放进自动填充;这只是发现凭证的交互模式,不能省略完整验证。
第五步:处理前端状态和失败
用户取消、超时、浏览器不支持、权限策略阻止和服务器拒绝要分别记录为用户可理解的状态。AbortController 可在用户离开页面或重复点击时取消尚未完成的调用,避免旧 Promise 覆盖新状态。前端不要显示签名、credential ID 或服务器内部错误,也不要在失败时自动把一次未完成注册标记为成功。
第六步:恢复、撤销和凭证管理
账户页面应列出凭证名称、创建时间和最近使用时间,允许用户删除单个凭证。服务器删除后,下一次收到未知 credential ID 可以调用 PublicKeyCredential.signalUnknownCredential();成功登录后也可用 signalAllAcceptedCredentials() 同步服务器仍接受的 ID。丢失所有凭证时走密码加风险校验、已验证邮箱或人工支持等恢复路径,并撤销旧会话后要求注册新 Passkey。
第七步:验证与演进
测试至少覆盖不同浏览器、平台 authenticator、跨设备同步、安全密钥、取消、超时、origin 错误、过期 challenge、重复 challenge、计数器异常和恢复后的旧凭证。配置通过能力检测和服务器策略下发,避免用 user-agent 猜测。新扩展采用可选字段和回退 UI,不让未知扩展改变服务器的核心验证规则。
高质量示范回答
我会把 Passkey 当作服务器驱动的公钥认证流程。用户点击注册时,前端先向服务器申请一次性 challenge 和 publicKey options,再在 HTTPS 页面调用 WebAuthn。返回结果只由服务器验证:challenge、origin、RP ID、签名、公钥算法和需要的 attestation 都通过后,服务器保存 credential ID、公钥、计数器和账户关联。私钥始终留在 authenticator。
登录流程同样先由服务器生成新的 challenge。前端调用 navigator.credentials.get(),收到 assertion 后提交给服务器;服务器验证签名、challenge、origin、RP ID 和计数器,成功后才创建会话。支持条件式 mediation 时,我会在用户名输入框交互后提供 Passkey 自动填充,但仍走相同的服务器验证。
前端把不支持、用户取消、超时和拒绝分开处理,并在离开页面时取消未完成调用。账户设置提供凭证列表和删除功能;删除后同步 WebAuthn 信号,让浏览器不再继续建议未知凭证。用户丢失设备时需要风控恢复、撤销旧会话、通知用户并注册新凭证。测试覆盖跨平台 authenticator、错误 origin、过期或重复 challenge、计数器异常、浏览器回退和恢复后的旧凭证,确保 UX 变化不会削弱协议验证。
常见错误
- 错误表现: 前端自己生成 challenge 或把它写在页面常量里。→ 失败原因: 攻击者可以重放旧断言,服务器也无法确认流程新鲜度。→ 修正方法: 由服务器生成、短期保存、一次消费并绑定账户和用途。
- 错误表现: 只检查 Promise 成功就登录。→ 失败原因: 浏览器返回对象不等于服务器已验证 origin、RP ID 和签名。→ 修正方法: 服务器完成完整断言校验,前端只根据结构化结果更新 UI。
- 错误表现: 用 user-agent 判断是否支持 Passkey。→ 失败原因: 浏览器版本、平台 authenticator 和权限策略会变化,静态判断容易误导用户。→ 修正方法: 使用能力 API 和服务器策略,失败时提供明确的密码或安全密钥路径。
- 错误表现: 删除数据库中的 credential 后不处理设备端状态。→ 失败原因: authenticator 仍认为凭证存在,用户会反复看到无法使用的选项。→ 修正方法: 在合适的认证状态下调用 WebAuthn signal API,并给出重新注册入口。
追问及应对
追问一:为什么 RP ID 不能直接写成任意 API 域名?
RP ID 必须与当前 origin 的域名关联,并由浏览器和服务器共同验证。把它写成不受当前页面控制的域名会导致创建流程失败或扩大信任范围。多租户场景要为每个可信域明确配置,不能用客户端传入的任意字符串。
追问二:用户在手机注册后,桌面浏览器没有这把钥匙怎么办?
如果产品允许同步的 discoverable credential,浏览器和凭证管理器可以在设备间提供同一账户的 Passkey;否则提供跨设备 QR 或安全密钥流程。前端应展示“此装置不可用”的可操作提示,服务器仍按相同的 challenge 和 assertion 规则验证,不因跨设备 UX 而跳过 origin 检查。
追问三:签名计数器倒退是否立即锁号?
先区分平台同步凭证、备份恢复和真正的克隆风险。计数器异常应触发风险标记、追加验证和通知,而不是在没有上下文时永久锁死账户。对高价值动作可以要求另一把已登记凭证或人工复核,并记录处理结果供后续策略调整。
追问四:恢复流程会不会把 Passkey 的安全性变弱?
会,如果恢复只依赖可被接管的邮箱链接。恢复应采用分级风控、会话撤销、冷却期、通知和高风险操作延迟;恢复成功后让用户登记新凭证并清理旧凭证。面试中要说明可用性和接管风险的取舍,而不是承诺“永远不会丢号”。