webauthn通过navigator.credentials对象实现无密码认证,核心方法是create()和get()。1. 注册时调用create()生成密钥对,私钥存于认证器,公钥发送服务器;2. 登录时调用get()获取签名断言,发送服务器验证身份。其安全性依赖于公钥加密机制,挑战值防止重放攻击,服务器需严格验证签名、来源、rp id等信息,并检查计数器防克隆。开发中需注意跨域配置、错误处理、兼容性测试及提供备用恢复机制。
在BOM(浏览器对象模型)中操作WebAuthn功能,主要围绕navigator.credentials这个全局对象展开。它提供了一套标准化的API,允许网页与用户的硬件安全密钥、指纹识别器或面部识别系统等认证器进行交互,从而实现无密码或多因素认证。简而言之,WebAuthn就是通过这个接口,让你的浏览器能“听懂”并“指挥”本地的安全硬件。
要实现WebAuthn的注册(创建新凭证)和认证(获取现有凭证),你主要会用到navigator.credentials.create()和navigator.credentials.get()这两个方法。这两个方法都返回一个Promise,你需要异步处理它们的结果。
注册(创建凭证): 当用户首次使用WebAuthn注册时,你需要调用navigator.credentials.create()。这个方法会指示浏览器生成一对新的公钥/私钥,私钥安全地存储在用户的认证器(如指纹传感器、USB密钥)中,而公钥则返回给你的Web应用,你需要将其发送到服务器进行存储。
const publicKeyCredentialCreationOptions = { challenge: Uint8Array.from('一个随机生成的挑战值,每次注册都应不同', c => c.charCodeAt(0)), // 服务器生成的随机数 rp: { name: "我的酷炫网站", // 网站名称 id: "yourdomain.com", // 网站域名,用于防止钓鱼 }, user: { id: Uint8Array.from('用户唯一ID', c => c.charCodeAt(0)), // 用户的唯一标识符 name: "user@example.com", // 用户名 displayName: "示例用户", }, pubKeyCredParams: [{ alg: -7, type: "public-key" }], // 推荐的算法类型,-7代表ES256 timeout: 60000, // 超时时间 attestation: "direct", // 或 "none", "indirect" excludeCredentials: [], // 可选,排除用户已注册的凭证 authenticatorSelection: { authenticatorAttachment: "platform", // "platform" (内置指纹) 或 "cross-platform" (USB密钥) userVerification: "required", // "required", "preferred", "discouraged" }, }; navigator.credentials.create({ publicKey: publicKeyCredentialCreationOptions }) .then(credential => { // 将 credential.response.attestationObject 和 credential.response.clientDataJSON // 以及 credential.id 发送到服务器进行验证和存储 console.log("注册成功:", credential); // 通常需要将 credential.rawId (Base64Url编码), credential.response.clientDataJSON, // credential.response.attestationObject, credential.type 等发送到后端 // 后端会验证这些数据,并存储公钥 }) .catch(error => { console.error("注册失败:", error); // 处理用户取消、超时、不支持等错误 });
认证(获取凭证): 当用户尝试登录时,你需要调用navigator.credentials.get()。这个方法会提示用户通过他们的认证器(如触摸指纹传感器)来证明身份。成功后,浏览器会返回一个签名过的断言(assertion),你的Web应用需要将其发送到服务器进行验证。
const publicKeyCredentialRequestOptions = { challenge: Uint8Array.from('一个随机生成的挑战值,每次认证都应不同', c => c.charCodeAt(0)), // 服务器生成的随机数 allowCredentials: [], // 可选,指定允许使用的凭证ID列表 timeout: 60000, userVerification: "preferred", // "required", "preferred", "discouraged" }; navigator.credentials.get({ publicKey: publicKeyCredentialRequestOptions }) .then(credential => { // 将 credential.response.authenticatorData, credential.response.clientDataJSON, // credential.response.signature, credential.response.userHandle // 以及 credential.id 发送到服务器进行验证 console.log("认证成功:", credential); // 后端会根据之前存储的公钥,验证这个断言的签名是否有效 }) .catch(error => { console.error("认证失败:", error); // 处理用户取消、超时、不支持等错误 });
WebAuthn之所以能实现无密码登录,其核心在于它巧妙地利用了公钥密码学。我们不再需要记忆复杂的密码,而是依赖于用户持有的“物理凭证”——一个存储了私钥的安全设备,比如你的手机指纹传感器、Windows Hello,或者一个外置的USB安全密钥(如YubiKey)。
想象一下这个过程:当你在一个支持WebAuthn的网站注册时,你的浏览器会和你的认证器(比如指纹识别器)“商量”一下,然后生成一对加密密钥:一个公钥和一个私钥。私钥就像一把只有你和你的认证器知道的“秘密钥匙”,它永远不会离开你的设备。公钥则像一把可以公开的“锁”,它被发送到网站的服务器上存储起来。
下次你登录时,网站会给你一个随机的“挑战”(challenge),就像一个只有这一次有效的谜题。你的浏览器会把这个谜题发给你的认证器,认证器用它的“秘密钥匙”(私钥)来“解答”这个谜题,并生成一个“答案”(签名),这个答案再发回给网站。网站用它存储的你的“公开锁”(公钥)来验证这个“答案”是否正确。如果答案正确,就证明是你在操作,因为只有拥有那个“秘密钥匙”的认证器才能生成正确的答案。
这个过程中,你的实际“密码”(私钥)从未离开过你的设备,也从未在网络上传输,这极大降低了密码被窃取或钓鱼攻击的风险。这不就是一种优雅的魔法吗?从用户的角度看,只需轻轻一触或一眼扫过,就完成了身份验证,既安全又便捷。
虽然WebAuthn前景光明,但在实际落地过程中,开发者确实会遇到一些让人头疼的“坑”。这不像传统的密码认证那样直白,它涉及到多方协作(浏览器、认证器、RP服务器),以及对公钥密码学的一些理解。
一个最常见的挑战是跨域问题(Cross-Origin Issues)。WebAuthn对rp.id(Relying Party ID,通常是你的域名)有严格的要求。它必须是当前页面的域名或其可注册的父域。比如,如果你的网站是app.example.com,那么rp.id可以是app.example.com或example.com。但如果你在dev.example.com上测试,而rp.id设置成了prod.example.com,浏览器就会拒绝操作。这是为了防止钓鱼攻击,但初次接触时很容易混淆。我的建议是,开发和生产环境的rp.id要仔细匹配,或者在开发时使用与开发域名一致的rp.id。
其次是用户体验与错误处理。WebAuthn的交互是高度依赖浏览器和操作系统的。用户可能会看到各种提示,比如“请触摸您的安全密钥”、“请验证您的指纹”。如果用户取消了操作、设备不支持、或者认证超时,navigator.credentials.create()或get()的Promise就会被拒绝。你需要捕获这些错误,并向用户提供清晰的反馈,而不是简单地抛出一个通用错误信息。例如,DOMException中的name属性可以告诉你具体是NotAllowedError(用户取消)还是SecurityError(协议问题)。
再来是挑战值(Challenge)的生成和验证。这个挑战值必须是服务器端生成的、加密安全的随机数,并且每次注册或认证都必须是唯一的。它只应使用一次(nonce),以防止重放攻击。如果服务器端没有正确地生成和验证挑战,整个WebAuthn的安全性就形同虚设。这部分是后端的核心安全逻辑,前端只负责传递。
最后,浏览器和设备兼容性也是一个持续的考量。虽然主流浏览器(Chrome, Firefox, Edge, Safari)都支持WebAuthn,但它们对某些特性(如authenticatorSelection.authenticatorAttachment、attestation)的支持程度可能有所不同。此外,用户的操作系统版本和硬件认证器类型也会影响功能可用性。在开发时,最好在多种浏览器和设备上进行测试,并考虑提供优雅降级方案,比如在不支持WebAuthn时回退到密码登录。这不像写一个简单的JavaScript函数那样,它需要对整个生态系统有更全面的认识。
WebAuthn的核心价值在于其安全性,但这种安全性并非自动获得,它需要前端和后端协同工作,并遵循一系列最佳实践。如果处理不当,即使是WebAuthn也可能存在漏洞。
一个至关重要的环节是服务器端对认证断言(Assertion)的严格验证。当navigator.credentials.get()返回一个PublicKeyCredential对象给前端时,前端必须将其发送到后端。后端不能简单地相信前端传来的数据。它需要:
此外,安全存储公钥也至关重要。公钥本身不是秘密,但它们必须与正确的用户关联,并存储在一个安全的数据库中。如果公钥被篡改或丢失,用户的WebAuthn认证将失效。
防止钓鱼攻击是WebAuthn设计的初衷之一,但开发者仍需警惕。rp.id的正确设置和验证是关键。如果攻击者能诱骗用户在钓鱼网站上进行WebAuthn操作,并成功注册或认证,那将是灾难性的。因此,除了技术实现,对用户的安全教育也同样重要。
最后,提供备用恢复机制是不可忽视的一点。虽然WebAuthn非常安全和方便,但如果用户的认证器丢失、损坏,或者他们无法访问,他们将无法登录。因此,提供传统的密码登录作为备用选项,或者提供基于邮件/短信的恢复流程,都是必要的。这就像给你的房子除了智能锁,还留了一把传统的钥匙,以防万一。毕竟,技术再先进,也得考虑真实世界中的各种“意外”。
以上就是BOM中如何操作浏览器的WebAuthn功能?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号