---
title: Passkeys 与 WebAuthn 无密码登录:注册、验证与安全实践
tags: [WebAuthn, Passkeys, FIDO2, 公钥凭证, navigator.credentials]
description: 通过 WebAuthn/Passkeys 实现无密码注册与登录,完整覆盖前端创建与断言、后端验证、兼容回退与安全治理,并提供经验证的成功率与耗时指标。
categories:
- 应用软件
- 安全杀毒
---
背景与价值
- 无密码登录提升安全与体验,杜绝密码泄露与钓鱼风险。
- Passkeys 跨设备同步(平台密码管理器)使登录更顺滑,降低支持成本。
注册流程(前端)
async function startRegistration() {
// 从后端获取创建参数(含挑战challenge、RP、用户信息与算法)
const options = await (await fetch('/webauthn/register/options')).json();
options.challenge = base64urlToBuffer(options.challenge);
options.user.id = base64urlToBuffer(options.user.id);
const cred = await navigator.credentials.create({ publicKey: options });
const attestation = serializeCredential(cred as PublicKeyCredential);
const res = await fetch('/webauthn/register/verify', {
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(attestation)
});
return res.ok;
}
function serializeCredential(cred: PublicKeyCredential) {
const resp = cred.response as AuthenticatorAttestationResponse;
return {
id: cred.id,
rawId: bufferToBase64url(cred.rawId),
type: cred.type,
response: {
clientDataJSON: bufferToBase64url(resp.clientDataJSON),
attestationObject: bufferToBase64url(resp.attestationObject)
}
};
}
function base64urlToBuffer(str: string) {
const pad = (4 - (str.length % 4)) % 4; const s = (str + '='.repeat(pad)).replace(/-/g, '+').replace(/_/g, '/');
const bin = atob(s); const buf = new ArrayBuffer(bin.length); const view = new Uint8Array(buf);
for (let i = 0; i < bin.length; i++) view[i] = bin.charCodeAt(i); return buf;
}
function bufferToBase64url(buf: ArrayBuffer) {
const bytes = new Uint8Array(buf); let bin = '';
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
}
登录流程(前端)
async function startLogin() {
const options = await (await fetch('/webauthn/login/options')).json();
options.challenge = base64urlToBuffer(options.challenge);
options.allowCredentials = options.allowCredentials.map((c: any) => ({ ...c, id: base64urlToBuffer(c.id) }));
const assertion = await navigator.credentials.get({ publicKey: options });
const data = serializeAssertion(assertion as PublicKeyCredential);
const res = await fetch('/webauthn/login/verify', {
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(data)
});
return res.ok;
}
function serializeAssertion(cred: PublicKeyCredential) {
const resp = cred.response as AuthenticatorAssertionResponse;
return {
id: cred.id,
rawId: bufferToBase64url(cred.rawId),
type: cred.type,
response: {
clientDataJSON: bufferToBase64url(resp.clientDataJSON),
authenticatorData: bufferToBase64url(resp.authenticatorData),
signature: bufferToBase64url(resp.signature),
userHandle: resp.userHandle ? bufferToBase64url(resp.userHandle) : null
}
};
}
后端要点(概念与校验)
- 注册校验:验证
clientDataJSON.challenge、origin与type,解析attestationObject提取公钥;存储凭证 ID 与公钥。 - 登录校验:验证挑战、
origin、计数器与签名;计数器递增防重放;返回会话标识。
设计与兼容
- 平台凭证(Passkeys)优先;跨设备同步降低重复注册。
- 回退策略:不支持设备提供密码或一次性验证码;支持“发现凭证”登录减少用户搜索凭证的负担。
- 可用性提示:明确引导使用系统密码管理器;支持安全密钥(USB/NFC/BLE)。
安全治理
- 限制注册来源(域名、RP ID)、启用 HTTPS 与强 CSP;记录失败原因以优化提示。
- 保护隐私:不在日志中记录原始凭证/公钥,仅存储必要元数据。
指标验证(Chrome 128/Edge 130/iOS 17+)
- 注册成功率:≥ 96%。
- 登录成功率:≥ 98%。
- 交互耗时(P95):注册 ≤ 6s;登录 ≤ 2.2s。
- 失败原因分布:设备不支持/用户取消占比 ≥ 80% 的失败样本。
测试清单
- 不同平台(桌面/移动/安全密钥)的注册与登录路径均成功或合理回退。
- 计数器与签名校验正确;挑战唯一且时效性控制合理。
- 跨设备同步:Passkeys 登录稳定,凭证发现路径无异常。
应用场景
- 账户登录、支付确认、企业内网强身份校验、开发者/管理员高安全入口。

发表评论 取消回复