1. 引言
2023年知乎博客 WebAuthn: 真正的无密码身份认证 总结得很赞。
在数字时代,密码已成为人们日常生活和在线活动中不可或缺的一部分。尽管互联网已经发展了 20 多年,许多方面都有了巨大的改进,但只有密码,还是 20 年前的用法。
更准确的说,它的用户体验比 20 年前更差了。密码的强度现在要求越来越高,一般不能少于 8 个字符,还要包括特殊符号。另外,除了密码,通常还有其他验证(短信、图片识别、OTP 一次性密码等等)。
然而,即使变得如此麻烦,依然不能杜绝密码被盗、被破解、被钓鱼的风险。
通常,恶意用户与银行帐户、社交媒体帐户和其他敏感数据之间的唯一联系就是密码。
对于用户和开发人员来说,密码带来的相关问题越来越多。用户必须担心密码被网络钓鱼工具窃取,或者如果他们拥有帐户的网站受到威胁,他们的密码会在网上泄露。他们必须担心在没有专用密码管理工具的情况下创建和记住密码。开发人员必须担心通过系统传递密码并将其安全存储在数据库中的所有复杂性。
当前,与黑客相关的违规行为,有81%是利用了被盗或弱密码。
基于密码的身份验证的主要弱点之一是密码是共享秘密。密码是证明”你确实是你“的唯一密钥。如果黑客设法窃取它,他们就可以完全冒充你,除非你使用双因素身份验证的 28% 用户之一。
为了解决这些问题,WebAuthn 应运而生。
目前,越来越多的服务已经开始正式支持 WebAuthn。就在2023年 5 月,谷歌和微软同时宣布全面接入 WebAuthn,支持无密码登录。GitHub 也在几天前宣布开始公测无密码登录功能。
2. WebAuthn简介:公钥加密和 Web 身份验证
WebAuthn,全称 Web Authentication,是由 FIDO 联盟(Fast IDentity Online Alliance)和 W3C(World Wide Web Consortium)联合制定的一套新的身份认证标准,旨在为网络身份验证提供一种更强大、更安全的方式,使用户能够使用他们的设备(如手机、USB 密钥或生物识别器)来进行身份验证,而无需使用密码。该标准于 2019 年 3 月 4 日正式成为 W3C 的推荐标准。目前主流的浏览器已经支持 WebAuthn,包括 Chrome、Firefox、Edge 和 Safari,更详细的支持情况可以通过 https://webauthn.me/browser-support 查看。
为网站创建私钥-公钥对(称为凭据),而不是密码。私钥安全地存储在用户的设备上;公钥和随机生成的凭证 ID 被发送到服务器进行存储。然后,服务器可以使用该公钥来证明用户的身份。
公钥不是秘密的,因为如果没有相应的私钥,它实际上是无用的。服务器不接收任何秘密的事实对用户和组织的安全具有深远的影响。数据库对黑客不再那么有吸引力,因为公钥对他们来说没有用处。
注:FIDO 联盟是一个非营利性组织,由 Google、微软、苹果、三星、高通、芯片厂商、支付公司、银行、电信运营商、认证公司等组成,旨在为用户提供更安全、更简单的身份验证体验。
2.1 Web身份验证依赖
Web身份验证依赖三大属性:
- 1)强安全:身份验证最好由硬件安全模块支持,该模块可以安全地存储私钥并执行 WebAuthn 所需的加密操作。
- 2)限定范围:密钥对仅对特定来源有用,如浏览器 cookie。在“webauthn.guide”注册的密钥对不能在“evil-webauthn.guide”使用,从而减轻了网络钓鱼的威胁。
- 3)已认证:身份验证器可以提供证书,帮助服务器验证公钥确实来自他们信任的身份验证器,而不是欺诈来源。
3. WebAuthn工作原理
WebAuthn 的原理并不复杂,它的核心是基于公钥的加密技术。在 WebAuthn 中,用户的身份认证是通过公钥和私钥来实现的。这很像我们平常使用配置了公私钥的 SSH 登录服务器的过程,只不过 WebAuthn 是在浏览器中实现的。
3.1 WebAuthn组成部分
WebAuthn 由以下三个组成部分组成:
- 1)用户代理(User Agent):用户代理是指浏览器或者其他支持 WebAuthn 的客户端,它负责与用户进行交互,收集用户的身份认证信息,并将其发送给服务器。
- 2)身份验证器(Authenticator):身份验证器是指用于生成公钥和私钥的设备,如手机、USB 密钥或生物识别器。Windows Hello 和 macOS 的 Touch ID 也都是常见的身份验证器。
- 3)Relying Party:Relying Party 是指需要进行身份认证的网站或应用程序,它负责生成挑战(Challenge)并将其发送给用户代理,然后验证用户代理发送的签名结果。
上述三者在两个不同的用例(注册和认证)中协同工作,如下图所示。图中的各个实体之间的所有通信都由用户代理(通常是Web浏览器)处理。
3.2 WebAuthn的注册流程
在注册时,用户端会生成一对公钥和私钥。其中,私钥存储在本地,而公钥则发送给服务器,服务端会将公钥与用户账户进行关联。详细的流程如下图所示:
3.3 WebAuthn的认证流程
在认证时,用户端会使用私钥对服务器端发送的挑战(Challenge)进行签名,然后将签名结果发送给服务器。服务器端会使用公钥对签名结果进行验证,从而完成身份认证。详细的流程如下图所示:
可在 https://webauthn.me/ 上体验 WebAuthn 的注册和认证过程。
4. WebAuthn API
WebAuthn 主要涉及2个 API:
- 1)navigator.credentials.create():用于在注册阶段生成公钥和私钥。注册示例用法为:
const res = await navigator.credentials.create({ publicKey: { // 随机的、加密安全的、至少 16 个字节的数据 // challenge: 挑战是在服务器上生成的加密随机字节的缓冲区,并且需要防止“重放攻击”。 challenge: base64url.decode("<%= challenge %>"), // relying party 的信息 // rp: 这代表“依赖方”;它可以被视为描述 负责注册和验证用户的组织。 必须id是浏览器中当前域的子集。 rp: { name: "Awesome Corp", // Relying party 的名称 }, // user: 这是有关当前注册用户的信息。 身份验证器使用id将凭证与用户相关联。建议不要使用个人识别信息作为id,因为它可能存储在身份验证器中。 user: { id: base64url.decode("<%= id %>"), name: "<%= name %>", displayName: "<%= displayName %>", }, // authenticatorSelection: 此可选对象可帮助依赖方对允许注册的身份验证器类型进行进一步限制。在此示例中,我们表明我们想要注册一个cross-platform身份验证器(如 Yubikey),而不是像 Windows Hello 或 Touch ID 这样的身份验证器 。 authenticatorSelection: { userVerification: "preferred" }, // attestation: 从验证器返回的证明数据包含可用于跟踪用户的信息。 此选项允许服务器指示证明数据对此注册事件的重要性。值"none"表示服务器不关心证明。值"indirect"表示服务器将允许匿名证明数据。direct表示服务器希望接收来自验证者的证明数据。 attestation: "direct", // pubKeyCredParams: 这是一个对象数组,描述服务器可以接受哪些公钥类型。 这是COSEalg注册表中描述的号码;例如,表示服务器接受使用 SHA-256 签名算法的椭圆曲线公钥。 pubKeyCredParams: [ { type: "public-key", alg: -7, // "ES256" IANA COSE Algorithms registry }, ], }, }); // 由于 res 并不是一个 JSON object,需要将 res 转换为 JSON object const json = publicKeyCredentialToJSON(res); // 将 json 发送给服务器 await post("/webauthn/register", { state: "<%= state %>", provider: "<%= provider %>", res: JSON.stringify(json), });
- 2)navigator.credentials.get():用于在认证阶段对服务端的挑战(Challenge)进行签名。登录示例用法为:
const res = await navigator.credentials.get({ publicKey: { // 随机的、加密安全的、至少 16 个字节的数据 // challenge: 与注册期间一样,这必须是在服务器上生成的加密随机字节。 challenge: base64url.decode("<%= challenge %>"), allowCredentials: [ { // id: 新生成的凭证的ID;它将用于 在对用户进行身份验证时识别凭证。此处提供的 ID 作为 base64 编码的字符串。 id: base64url.decode("<%= id %>"), type: "public-key", }, ], // timeout: 在返回错误之前用户必须响应注册提示的时间(以毫秒为单位) 。 timeout: 15000, // 超过 15 秒未完成认证,则认为认证失败 authenticatorSelection: { userVerification: "preferred" }, }, }); // 由于 res 并不是一个 JSON object,需要将 res 转换为 JSON object const json = publicKeyCredentialToJSON(res); // 将 json 发送给服务器 post("/webauthn/authenticate", { state: "<%= state %>", provider: "<%= provider %>", res: JSON.stringify(json), });
参考资料
[1] 2023年知乎博客 WebAuthn: 真正的无密码身份认证
[2] WebAuthn官网