【无标题】

WebAuthn

  • 全称是 Web Authentication,是一个由万维网联盟(W3C)和 FIDO 联盟(Fast Identity Online)开发的 web 标准。它允许网站和应用程序使用公钥加密和生物识别技术(如指纹识别、面部识别)进行强身份验证,以替代传统的密码登录方式。WebAuthn 提供了更高的安全性和更好的用户体验。
  • https://webauthn.io/

WebAuthn 工作原理

WebAuthn 是一个由万维网联盟(W3C)和 FIDO 联盟(Fast Identity Online)开发的 web 标准,它允许网站和应用程序使用公钥加密和生物识别技术(如指纹识别、面部识别)进行强身份验证,以替代传统的密码登录方式。以下是 WebAuthn 的工作原理,包括注册(Registration)和认证(Authentication)两个主要过程。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1. 注册(Registration)

在注册过程中,用户在其设备上创建一个新的公钥-私钥对,并将公钥发送给服务器保存。具体步骤如下:

  1. 用户发起注册请求:用户在网站上选择注册,并开始注册过程。
  2. 服务器生成挑战(challenge):服务器生成一个随机的挑战,并将其发送给用户的浏览器。
  3. 浏览器调用 WebAuthn API:浏览器使用 WebAuthn API 调用用户设备的可信平台模块(TPM)或安全元件,生成公钥-私钥对。然后,设备会用私钥签署服务器的挑战,并将签名和公钥一起发送回服务器。
  4. 服务器验证并存储公钥:服务器验证签名和挑战,以确保请求的真实性。如果验证成功,服务器存储公钥用于后续的认证。

2. 认证(Authentication)

在认证过程中,用户使用之前注册的设备进行登录。具体步骤如下:

  1. 用户发起登录请求:用户在网站上选择登录,并开始认证过程。
  2. 服务器生成挑战(challenge):服务器生成一个随机的挑战,并将其发送给用户的浏览器。
  3. 浏览器调用 WebAuthn API:浏览器使用 WebAuthn API 调用用户设备的 TPM 或安全元件,使用私钥签署服务器的挑战。然后,设备将签名和相关信息发送回服务器。
  4. 服务器验证签名:服务器使用存储的公钥验证签名和挑战,以确保请求的真实性。如果验证成功,用户成功登录。

WebAuthn 的优势

  1. 提高安全性:WebAuthn 使用公钥加密,减少了传统密码的攻击面,避免了密码泄露、弱密码等问题。
  2. 简化用户体验:利用设备的生物识别功能,如指纹、面部识别等,使得用户无需记住复杂的密码,登录过程更加便捷。
  3. 广泛支持:WebAuthn 标准得到了主流浏览器和操作系统的广泛支持,包括 Chrome、Firefox、Edge、Safari,以及 Android 和 iOS 设备。

navigator.credentials.create

  • Navigator 接口的只读属性 credentials 返回与当前文档关联的 CredentialsContainer 对象,该对象暴露用于请求凭据的方法。CredentialsContainer 接口还会在发生感兴趣的事件时通知用户代理,例如成功登录或注销。此接口可用于特性检测。

开始注册

Web Authentication API 的 options 对象
字段类型说明可选值
attestation字符串指定所需的认证声明传递偏好。"none"(无需认证信息)、"indirect"(间接认证)、"direct"(直接认证)
authenticatorSelection对象定义选择认证器的标准。
- authenticatorAttachment字符串指定要使用的认证器类型。"platform"(使用集成到设备中的认证器)、"cross-platform"(使用外部认证器)
- requireResidentKey布尔值指示认证器是否必须创建存储在设备上的常驻密钥。true(是)、false(否)
- residentKey字符串指定常驻密钥的要求。"required"(必须)、"preferred"(优先)、"discouraged"(不建议)
challenge字符串用于认证过程中的挑战码,应该是从服务器端生成的。
excludeCredentials数组列出在注册新凭证时应排除的凭证的列表,用于确保不重复注册。
extensions对象用于扩展 Web Authentication API 的附加选项。
- credProps布尔值指示是否返回有关创建的凭证的属性信息。true(返回),false(不返回)
pubKeyCredParams数组定义公钥证书应使用的加密算法。
- alg数字算法标识符(如 -7 表示 ES256,-257 表示 RS256)
- type字符串证书类型,通常为 "public-key"
rp对象定义依赖方(即服务提供者)的信息。
- id字符串依赖方的域名。
- name字符串依赖方的描述名称,如 "Passkey Example" 本地测试可以用 window.location.hostname
timeout数字指定操作的超时时间(毫秒)。
user对象定义正在注册或认证的用户的信息。
- displayName字符串用户的显示名称。
- id字符串用户的唯一标识符。
- name字符串用户的名称。
  // options 参考
  
  {
          attestation: "none",
          authenticatorSelection: {
            authenticatorAttachment: "platform",
            requireResidentKey: true,
            residentKey: "required",
          },
          challenge: "F16NrxGy23Ps_73Pgy_H6fh0ihgEXBuycusFLahMvmU",
          excludeCredentials: [],
          extensions: {
            credProps: true,
          },
          pubKeyCredParams: [
            {
              alg: -7, // 对应于ES256
              type: "public-key",
            },
            {
              alg: -257, // 对应于RS256
              type: "public-key",
            },
          ],
          rp: {
            id: window.location.hostname,  // 本地测试用 可以使用localhost
            name: "Passkey Example",
          },
          timeout: 60000,
          user: {
            displayName: "allen",
            id: "20222027",
            name: "allen",
          },
        };
  
需要将 challenge 和 user.id 转成ArrayBuffer 类型
options.challenge = base64url.decode(options.challenge); 
options.user.id = base64url.decode(options.user.id);


const base64url = {
  encode: function (buffer) {
    const base64 = window.btoa(String.fromCharCode(...new Uint8Array(buffer)));
    return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
  },
  decode: function (base64url) {
    const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
    const binStr = window.atob(base64);
    const bin = new Uint8Array(binStr.length);
    for (let i = 0; i < binStr.length; i++) {
      bin[i] = binStr.charCodeAt(i);
    }
    return bin.buffer;
  },
};

唤起生物认证
  • 未开启 windows hello

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 在系统设置里面添加

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 添加完成  唤起成功

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

完成注册后的步骤

在 Web Authentication API (navigator.credentials.create) 成功创建公钥凭证后,你需要执行以下几个关键步骤以完成用户的注册过程:

1. 发送凭证信息到服务器

成功创建凭证后,需要将其详细信息发送到服务器端进行验证和存储。以下是应发送的凭证详情:

  • ID (credential.id): 凭证的唯一标识符。
  • 类型 (credential.type): 凭证的类型,通常是 "public-key"
  • 原始 ID (credential.rawId): 凭证的原始标识符,使用 Base64URL 编码格式。
  • 响应 (credential.response): 包括认证对象 (attestationObject) 和客户端数据 (clientDataJSON) 的响应,均使用 Base64URL 编码。
2. 服务器端验证

服务器接收到客户端发送的凭证信息后,需要执行以下验证步骤:

  • 验证挑战 (challenge): 确认返回的挑战码与之前发送给客户端的挑战码相匹配。
  • 验证来源 (origin): 确认凭证的创建请求来自预期的域名。
  • 验证公钥凭证参数 (pubKeyCredParams): 确认凭证使用了有效的加密算法。
  • 存储凭证信息: 在服务器端安全地存储用户的公钥和其他相关凭证信息,以备将来进行身份验证使用。
示例代码:将凭证信息发送到服务器
fetch('/api/register', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({
        id: credential.id,
        type: credential.type,
        rawId: credential.rawId,
        attestationObject: credential.response.attestationObject,
        clientDataJSON: credential.response.clientDataJSON
    })
})
.then(response => response.json())
.then(data => {
    console.log('注册成功', data);
    alert('注册成功!');
})
.catch(error => {
    console.error('注册失败', error);
    alert('注册失败,请重试!');
});

完整注册代码
    async register() {
      if (!("credentials" in navigator)) {
        alert(
          "此浏览器不支持 Web Authentication API。请尝试更新浏览器或使用其他支持的浏览器。"
        );
        return;
      }

      try {
        // 从服务器获取创建凭证的选项
        const {data: options} = await axios.get('/api/register-options');

        // 转成 arrayBuffer
        options.challenge = base64url.decode(options.challenge);
        options.user.id = base64url.decode(options.user.id);

        // 开始注册
        const credential = await navigator.credentials.create({
          publicKey: options,
        });
        
        await axios.post('/api/register', {
         id: credential.id,
         type: credential.type,
         rawId: credential.rawId,
         attestationObject: credential.response.attestationObject,
         clientDataJSON: credential.response.clientDataJSON
        });
        
        alert("注册成功");
      } catch (error) {
        console.error("注册失败", error);
        alert("注册失败");
      }
    },

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值