3. WebAuthn核心架构

WebAuthn核心架构 学习卡片

要真正理解Passkey的安全边界,必须先拆开WebAuthn的架构。WebAuthn不是单一软件,而是一套由浏览器、操作系统、硬件认证器与业务服务器共同履约的协议。它把“证明用户是谁”从密码比对,转变为“证明用户持有某个私钥”。这个转变的关键,在于四个角色和一条挑战-响应链路。

一、四个核心角色与公钥凭证

WebAuthn规范定义了三个直接参与方,加上一个承载标准的上层联盟:

  • 依赖方(Relying Party,RP):即你的业务服务器,例如银行App后端。RP负责生成挑战、调用WebAuthn API、验证签名,并保存公钥凭证。RP的域名(RP ID)是安全边界,通常为注册域的eTLD+1,如 example.com。
  • 用户代理(User Agent,UA):浏览器或WebView。UA负责调用平台API、传递挑战、协调认证器,并执行同源策略与RP ID校验。UA不接触私钥,只做“传令兵”和“守门人”。
  • 认证器(Authenticator):真正生成和保存密钥的实体。它可以是平台认证器(Touch ID、Windows Hello、Android Keystore),也可以是跨平台认证器(YubiKey等安全密钥)。认证器内部有不可导出的私钥,并可能要求生物识别或PIN解锁。
  • FIDO2标准:由W3C WebAuthn与FIDO联盟CTAP2共同组成。WebAuthn定义浏览器与RP之间的JS API;CTAP2定义浏览器与外部认证器之间的通信协议。两者合起来,才是完整的FIDO2认证能力。

注册成功后,RP保存的不是密码,而是公钥凭证(Public Key Credential)。它包含凭证ID、公钥、签名计数器、AAGUID等元数据。私钥永远留在认证器内,即使RP数据库被拖库,攻击者也无法伪造签名。

核心结论:WebAuthn的安全模型是“私钥不出认证器,公钥可公开存储”。RP验证的是签名,而不是秘密本身。

二、挑战-响应模型:一次完整认证的流程

挑战-响应是WebAuthn的骨架。RP每次认证都生成一个随机、一次性、有时效的challenge,认证器用私钥对“挑战+客户端数据+认证器数据”做签名,RP再用公钥验签。攻击者重放旧签名无效,因为挑战已变。

  1. 用户点击“使用Passkey登录”,RP生成challenge并返回给前端。
  2. 前端调用navigator.credentials.get(),传入publicKey参数,包括challenge、rpId、allowCredentials等。
  3. UA校验RP ID与当前源匹配,弹出认证器选择界面。
  4. 认证器要求用户验证(生物识别/PIN),用私钥签名authenticatorData + SHA-256(clientDataJSON)。
  5. RP收到AuthenticatorAssertionResponse,校验挑战、源、RP ID哈希、签名和计数器。
  6. 校验通过则建立会话,否则拒绝。
// 前端发起认证的核心参数(简化)
const publicKey = {
  challenge: base64urlToBuffer(serverChallenge),
  rpId: "example.com",
  allowCredentials: [{
    id: base64urlToBuffer(credentialId),
    type: "public-key",
    transports: ["internal", "hybrid"]
  }],
  userVerification: "preferred",
  timeout: 60000
};
const assertion = await navigator.credentials.get({ publicKey });
// assertion.response.signature 发送给RP验签

注册流程与认证对称:RP下发challenge和用户信息,认证器生成新密钥对,返回AttestationObject和clientDataJSON,RP验证后保存公钥凭证。注册时的attestation可用于确认认证器型号,但Passkey场景通常采用none以保护隐私。

三、FIDO2标准关系与典型场景

很多团队把WebAuthn、FIDO2、CTAP2混为一谈。下表给出清晰边界:

标准/组件 作用范围 典型载体 是否直接处理私钥
WebAuthn RP与UA之间的JS API 浏览器、WebView 否
CTAP2 UA与外部认证器之间的协议 USB/NFC/蓝牙安全密钥 否
平台认证器 操作系统内置密钥管理 Touch ID、Windows Hello 是(内部)
RP服务器 挑战生成、验签、凭证存储 业务后端 否(仅存公钥)

典型场景:用户在手机上用Face ID注册Passkey,RP保存公钥。换到笔记本登录时,浏览器通过混合传输(hybrid)调用手机上的认证器,手机弹出Face ID确认,签名后回传。整个过程中,私钥从未离开手机安全芯片,笔记本和服务器都只看到签名结果。

四、常见误区与复盘要点

误区一:把RP ID当成普通配置。RP ID一旦上线就不能随意更改,否则所有已注册凭证失效。它必须是当前源的可注册域后缀,且不能是IP地址。

  • 误区二:忽略挑战的随机性与时效。挑战必须由密码学安全随机数生成,且一次性使用。若复用或过期时间过长,重放攻击窗口会打开。
  • 误区三:只验签名不验来源。必须校验clientDataJSON.origin与预期源一致,并校验rpIdHash,否则钓鱼站点可能诱导签名。
  • 误区四:认为Passkey等于无密码。Passkey可以作为多因素之一,也可以配合PIN。是否“无密码”取决于RP的认证策略,而非协议本身。
  • 误区五:忽视计数器与克隆检测。部分认证器提供签名计数器。若计数器回退,可能意味着凭证被克隆,应触发风控或吊销。

复盘建议:在实现时,把“挑战生成、验签、源校验、RP ID哈希校验、计数器校验”做成五个独立可测试的单元。任何一步缺失,都会让WebAuthn退化为“带签名的密码”。