userVerification 深度解析

本文档讨论了 WebAuthn 中的 userVerification 是什么,以及在通行密钥创建或身份验证期间指定了 userVerification 时产生的浏览器行为。

什么是“用户验证”?

通行密钥是基于公钥加密构建的。创建通行密钥后,系统会生成公钥私钥对,私钥由通行密钥提供程序存储,而公钥会返回到依赖方 (RP) 服务器进行存储。服务器可以通过使用配对的公钥验证由同一通行密钥签名的签名来验证用户身份。“用户呈现”(UP) 标志可证明有人在身份验证期间与设备进行了互动。

用户验证是一道可选的安全防护,旨在如“用户存在状态”所声明的那样,旨在断言身份验证期间存在的确实是正确的人员,而不仅仅是某个人。在智能手机上,通常需要通过屏幕锁定(无论是采用生物识别技术还是输入 PIN 码/密码)来锁定设备。“UV”中会报告是否执行了用户验证通行密钥注册和身份验证期间在身份验证器数据中返回的标志

<ph type="x-smartling-placeholder">
</ph> 屏幕截图:macOS 上 iCloud 钥匙串上的用户验证对话框。该对话框会提示用户使用触控 ID 登录,并显示请求身份验证的源站及用户名。对话框的右上角有一个标记为“Cancel”的按钮。 <ph type="x-smartling-placeholder">
</ph> macOS 上 iCloud 钥匙串上的用户验证对话框。
<ph type="x-smartling-placeholder">
</ph> Android 版 Chrome 中用户验证对话框的屏幕截图。该对话框会提示用户使用人脸识别或指纹检测验证身份,并显示请求身份验证的来源。左下角有一个使用 PIN 码进行验证的选项。
Android Chrome 上的用户验证对话框。

如何在服务器上验证 UP 和 UV

系统会在身份验证器数据字段中向服务器发送“用户状态”(UP) 和“用户已验证”(UV) 布尔值标记。在身份验证期间,可通过使用存储的公钥验证签名来验证身份验证器数据字段的内容。只要签名有效,服务器就可以认为标记是真实的。

<ph type="x-smartling-placeholder">
</ph> 身份验证数据结构的描绘。数据结构的每个部分从左到右依次为“RP ID HASH”(32 个字节)、“FLAGS”(1 字节),“COUNTER”(4 个字节,big-endian uint32),“ATTESTE CRED.DATA&#39;(如果有,则长度可变)和“EXTENSIONS”(如果存在长度可变 (CBOR))。“FLAGS”部分已展开,显示可能的标记列表,从左到右依次为:“ED”、“AT”、“0”、“BS”、“BE”、“UV”、“0”和“UP”。 <ph type="x-smartling-placeholder">
</ph> 公钥凭据中的身份验证器数据字段。

在通行密钥注册和身份验证时,服务器应根据要求检查 UP 标志是否为 true,以及 UV 标志是 true 还是 false

指定 userVerification 参数

根据 WebAuthn 规范,RP 可以在凭据创建和断言时使用 userVerification 参数请求用户验证。它接受 'preferred''required''discouraged',分别表示:

  • 'preferred'(默认):在设备上使用用户验证方法是首选方式,但如果无法使用该方法,则可以跳过。如果执行了用户验证,响应凭据包含的 UV 标志值为 true;如果未执行 UV,则包含 false 标志。
  • 'required':必须调用设备上可用的用户验证方法。如果其中一个不可用,则请求在本地失败。这意味着响应凭据始终会返回 UV 标志并将其设置为 true
  • 'discouraged':不建议使用用户验证方法。不过,仍可能会执行用户验证,具体取决于设备,并且 UV 标志可以包含 truefalse

通行密钥创建的示例代码:

const publicKeyCredentialCreationOptions = {
  // ...
  authenticatorSelection: {
    authenticatorAttachment: 'platform',
    residentKey: 'required',
    requireResidentKey: true,
    userVerification: 'preferred'
  }
};

const credential = await navigator.credentials.create({
  publicKey: publicKeyCredentialCreationOptions
});

通行密钥身份验证的示例代码:

const publicKeyCredentialRequestOptions = {
  challenge: /* Omitted challenge data... */,
  rpId: 'example.com',
  userVerification: 'preferred'
};

const credential = await navigator.credentials.get({
  publicKey: publicKeyCredentialRequestOptions
});

您应该为“userVerification”选择哪个选项?

您应使用的 userVerification 值取决于您的应用要求以及用户体验需求。

何时使用 userVerification='preferred'

如果您更重视用户体验而非保护,请使用 userVerification='preferred'

在某些环境中,用户验证比保护更困难。例如,在无法使用触控 ID(由于设备不支持触控 ID,或设备处于翻盖模式)的 macOS 上,系统会改为要求用户输入系统密码。这会造成不便,用户可能会完全放弃身份验证。如果消除阻力对您来说更重要,请使用 userVerification='preferred'

<ph type="x-smartling-placeholder">
</ph> 当触控 ID 不可用时显示的 macOS 通行密钥对话框的屏幕截图。该对话框包含请求身份验证的来源以及用户名等信息。对话框的右上角有一个标记为“Cancel”的按钮。 <ph type="x-smartling-placeholder">
</ph> 当触控 ID 不可用时,macOS 上显示的通行密钥对话框。

使用 userVerification='preferred' 时,如果成功执行用户验证,则 UV 标志为 true;如果跳过用户验证,则标记为 false。例如,在触控 ID 不可用的 macOS 上,它会要求用户点击某个按钮以跳过用户验证,并且公钥凭据包含 false UV 标志。

随后,紫外线标志可用作风险分析中的信号。如果登录尝试似乎由于其他因素而存在风险,则您可能需要要求用户进行额外的登录验证,前提是您未进行用户验证。

<ph type="x-smartling-placeholder">

何时使用 userVerification='required'

如果您认为 UP 和 UV 都绝对必要,请使用 userVerification='required'

这种方式的缺点是用户在登录时可能会遇到更多麻烦。例如,在不支持触控 ID 的 macOS 上,系统会要求用户输入系统密码。

借助 userVerification='required',您可以确保在设备上执行用户验证。确保服务器验证 UV 标志是否为 true

总结

通过利用用户验证,依赖通行密钥的方可以衡量设备所有者登录的可能性。他们可以自行决定是要求用户验证,还是设为可选,具体取决于回退登录机制对用户体验流程的重要程度。确保服务器会检查 UP 标志和 UV 标志以进行通行密钥用户身份验证。