WebAuthn/FIDO2 无密码认证最佳实践概述WebAuthn/FIDO2通过公钥注册与挑战签名实现无密码认证,提升安全性与用户体验。本文提供服务端与前端的最小实现与治理建议。注册流程(前端)async function startRegistration(username: string) {

const res = await fetch('/webauthn/register/options', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username }) })

const options = await res.json()

options.challenge = Uint8Array.from(atob(options.challenge), c => c.charCodeAt(0))

options.user.id = Uint8Array.from(atob(options.user.id), c => c.charCodeAt(0))

const cred = await navigator.credentials.create({ publicKey: options })

const att = (cred as PublicKeyCredential)

const payload = {

id: att.id,

rawId: btoa(String.fromCharCode(...new Uint8Array(att.rawId as ArrayBuffer))),

type: att.type,

response: {

clientDataJSON: btoa(String.fromCharCode(...new Uint8Array(att.response.clientDataJSON as ArrayBuffer))),

attestationObject: btoa(String.fromCharCode(...new Uint8Array(att.response.attestationObject as ArrayBuffer)))

}

}

await fetch('/webauthn/register/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) })

}

登录流程(前端)async function startLogin(username: string) {

const res = await fetch('/webauthn/login/options', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username }) })

const options = await res.json()

options.challenge = Uint8Array.from(atob(options.challenge), c => c.charCodeAt(0))

options.allowCredentials = options.allowCredentials.map((c: any) => ({ ...c, id: Uint8Array.from(atob(c.id), d => d.charCodeAt(0)) }))

const assertion = await navigator.credentials.get({ publicKey: options })

const cred = assertion as PublicKeyCredential

const payload = {

id: cred.id,

rawId: btoa(String.fromCharCode(...new Uint8Array(cred.rawId as ArrayBuffer))),

type: cred.type,

response: {

clientDataJSON: btoa(String.fromCharCode(...new Uint8Array(cred.response.clientDataJSON as ArrayBuffer))),

authenticatorData: btoa(String.fromCharCode(...new Uint8Array((cred.response as AuthenticatorAssertionResponse).authenticatorData as ArrayBuffer))),

signature: btoa(String.fromCharCode(...new Uint8Array((cred.response as AuthenticatorAssertionResponse).signature as ArrayBuffer))),

userHandle: btoa(String.fromCharCode(...new Uint8Array(((cred.response as any).userHandle || new ArrayBuffer(0)) as ArrayBuffer)))

}

}

await fetch('/webauthn/login/verify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) })

}

服务端要点(伪代码)// 生成挑战并存储到会话

function issueChallenge(): string {

const bytes = crypto.randomBytes(32)

return bytes.toString('base64')

}

// 验证注册:解析attestationObject并保存公钥(credentialId, publicKey, counter)

// 验证登录:校验challenge与签名、counter递增、防重放

运维要点在HTTPS下启用,前端与后端使用随机挑战并防重放绑定设备与账户,记录AAGUID与计数器用于风险检测对账号恢复提供备用登录(MFA/备份代码),并限制重置路径通过WebAuthn/FIDO2的挑战与签名流程,可在Web场景实现安全、便捷的无密码认证。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部