You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WebAuthn:如何识别错误指纹/PIN/图案验证尝试并限制验证次数

WebAuthn验证尝试次数限制与错误识别实现方案

一、错误尝试的识别方式

WebAuthn不会直接返回"指纹错误""PIN输错"这类具体失败原因,但可以通过两种核心方式判断验证失败:

  • 客户端API异常捕获:调用navigator.credentials.get()时,用户验证失败或主动取消操作,浏览器会抛出NotAllowedError或AbortError(不同浏览器存在差异,比如Chrome在验证失败时可能抛出AbortError,Firefox部分场景返回NotAllowedError)。
  • 服务端断言验证结果:客户端将验证后的断言发送到服务端后,通过WebAuthn验证库(如@simplewebauthn/server)校验时,若断言无效(比如签名不匹配),库会返回明确的错误信息,间接说明用户身份验证环节未通过。

二、尝试次数限制的实现

1. 客户端辅助限制(仅做体验优化)

可以在客户端用sessionStorage维护会话内的失败计数器,达到阈值后暂时禁用WebAuthn验证按钮。但这种方式不安全,用户可通过清空存储绕过,只能作为前端体验补充。

2. 服务端核心限制(必须实现)

这是最可靠的限制方式,需结合数据库存储用户验证状态:

  • 数据库字段设计:为用户表添加webauthn_failed_attempts(失败次数)和webauthn_lockout_until(锁定截止时间)两个字段。
  • 核心流程逻辑:
    • 用户发起验证请求时,先检查是否处于锁定状态(webauthn_lockout_until晚于当前时间),若是直接返回锁定提示。
    • 服务端验证断言失败时,递增webauthn_failed_attempts计数。
    • 当失败次数达到阈值(如5次),设置webauthn_lockout_until为当前时间+锁定时长(如15分钟),同时重置失败计数。
    • 验证成功时,将webauthn_failed_attempts重置为0,清除锁定时间。

3. 异常捕获的细节处理

客户端捕获NotAllowedError/AbortError后,可向服务端发送"验证失败"请求触发计数递增,但要注意区分用户主动取消和真实验证失败:比如可以通过记录弹窗显示时长,若用户短时间内关闭弹窗,不算错误尝试,具体逻辑根据业务场景调整。
服务端验证断言失败时直接递增计数是最准确的方式,因为只有用户完成身份验证环节但未通过,才会生成无效断言。

三、服务端代码示例(Node.js + @simplewebauthn/server)

// 假设用户模型包含webauthnFailedAttempts、webauthnLockoutUntil等字段
async function verifyWebAuthnAssertion(userId, assertion) {
  const user = await User.findById(userId);
  
  // 检查是否处于锁定状态
  if (user.webauthnLockoutUntil && new Date() < user.webauthnLockoutUntil) {
    throw new Error("WebAuthn验证已锁定,请15分钟后再试");
  }

  try {
    const verificationResult = await verifyAuthenticationResponse({
      response: assertion,
      expectedChallenge: user.latestWebAuthnChallenge,
      authenticator: user.webauthnAuthenticator,
      expectedOrigin: "https://your-domain.com",
      expectedRPID: "your-domain.com",
    });

    // 验证成功,重置状态
    user.webauthnFailedAttempts = 0;
    user.webauthnLockoutUntil = null;
    await user.save();

    return verificationResult;
  } catch (error) {
    // 验证失败,更新计数与锁定状态
    user.webauthnFailedAttempts += 1;
    if (user.webauthnFailedAttempts >= 5) {
      user.webauthnLockoutUntil = new Date(Date.now() + 15 * 60 * 1000);
      user.webauthnFailedAttempts = 0;
    }
    await user.save();
    throw error;
  }
}

内容的提问来源于stack exchange,提问作者canaryGrapher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 08:50:11