超越密码:走向无密码认证
Move beyond passwords
2021年6月10日
一句话判断
苹果在 WWDC 2021 正式预告了 Passkeys(基于 FIDO2/WebAuthn 的无密码认证)—— 密码终将被设备级密钥替代,虽然完整实现在后续版本才落地,但这集 Session 是你理解苹果身份认证路线图的关键节点。
这场 Session 讲了什么
Session 系统讲解了苹果在身份认证领域的三个方向:密码管理的改进、多因素认证的简化、以及 Passkeys(通行密钥)的概念预览。
密码方面,iCloud Keychain 新增了内置的密码泄露检测(Security Recommendations),如果用户的密码出现在已知的数据泄露列表中,系统会主动提醒更换。Safari 的自动填充也做了改进,支持更复杂的表单场景。
多因素认证方面,结合前面 Session 10105 讲到的 iCloud Keychain TOTP 功能,登录流程中的二步验证可以全自动完成。
Passkeys 方面,这是苹果对 FIDO Alliance(FIDO 联盟)提出的无密码认证方案的实现。用户的认证密钥存储在设备 Secure Element 中,通过 Face ID/Touch ID 触发,不需要输入任何密码。密钥通过 iCloud Keychain 在设备间同步。
值得深挖的点
Passkeys 的技术架构。Passkeys 基于 WebAuthn(Web Authentication)标准,但和传统 WebAuthn 的关键区别在于:传统 WebAuthn 的密钥绑定在单个设备上(需要额外的安全密钥硬件),而 Passkeys 通过 iCloud Keychain 同步到用户的所有设备。这解决了”丢失设备 = 丢失所有凭据”的致命问题。密钥使用 Secure Element 存储,永远不会暴露给网站或 app。
向后兼容性策略。Passkeys 不是一步到位替代所有密码。在 iOS 15 中,Safari 会优先尝试 Passkey 认证,如果网站不支持,回退到密码自动填充。对于开发者,苹果建议同时支持 Passkey 和传统密码登录,渐进式迁移。WebAuthn API 的 JavaScript 接口在 Safari 15 中已经可用。
代码片段
WebAuthn 注册 Passkey(Web 端):
// 网站端:注册新的 Passkey
async function registerPasskey() {
const challenge = new Uint8Array(32);
crypto.getRandomValues(challenge);
const credential = await navigator.credentials.create({
publicKey: {
challenge: challenge,
rp: { name: "My App", id: "myapp.com" },
user: {
id: new Uint8Array(16),
name: "user@example.com",
displayName: "示例用户"
},
pubKeyCredParams: [
{ type: "public-key", alg: -7 }, // ES256
{ type: "public-key", alg: -257 } // RS256
],
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "required"
}
}
});
// 将 credential 发送到服务端存储
await fetch('/api/register-passkey', {
method: 'POST',
body: JSON.stringify({
id: credential.id,
rawId: btoa(String.fromCharCode(...new Uint8Array(credential.rawId))),
type: credential.type
})
});
}
WebAuthn 认证(登录):
// 网站端:使用 Passkey 登录
async function authenticateWithPasskey() {
const challenge = new Uint8Array(32);
crypto.getRandomValues(challenge);
try {
const credential = await navigator.credentials.get({
publicKey: {
challenge: challenge,
rpId: "myapp.com",
userVerification: "required"
}
});
// 验证成功,发送到服务端确认
const response = await fetch('/api/verify-passkey', {
method: 'POST',
body: JSON.stringify({
id: credential.id,
rawId: btoa(String.fromCharCode(...new Uint8Array(credential.rawId))),
signature: btoa(String.fromCharCode(
...new Uint8Array(credential.response.signature)
))
})
});
// 登录成功
} catch (error) {
// 用户取消或认证失败,回退到密码登录
showPasswordLoginForm();
}
}
原生 App 中使用 Sign in with Apple 作为过渡方案:
import AuthenticationServices
// 在 Passkeys 完全可用之前,用 Sign in with Apple 作为无密码方案
func signInWithApple() {
let provider = ASAuthorizationAppleIDProvider()
let request = provider.createRequest()
request.requestedScopes = [.fullName, .email]
let controller = ASAuthorizationController(authorizationRequests: [request])
controller.delegate = self
controller.performRequests()
}
// 处理授权结果
func authorizationController(controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization) {
if let credential = authorization.credential as? ASAuthorizationAppleIDCredential {
let userID = credential.user
let email = credential.email
// 使用 userID 作为用户的唯一标识(跨设备一致)
signInUser(identifier: userID, email: email)
}
}
最佳实践
- 现在就开始在 Web 端支持 WebAuthn。即使 Passkeys 的完整体验在后续版本才到来,Safari 15 已经支持 WebAuthn API。提前接入可以覆盖使用安全密钥的高级用户。
- 同时保留密码登录作为后备。在 Passkeys 推广的过渡期(可能持续 3-5 年),密码仍然是必要的后备方案。但可以在 UI 上优先展示 Passkey 选项。
- 用 Sign in with Apple 填补移动端的空白。在 Passkeys 原生 SDK 完善之前,Sign in with Apple 是最接近”无密码”体验的方案。
还有什么值得关注
- Passkeys 是苹果、Google、Microsoft 三方联合推动的标准,跨平台兼容性是核心设计目标。
- iCloud Keychain 的密码泄露检测使用差分隐私技术,苹果不会知道你的具体密码。
- 苹果建议在注册新用户时直接引导创建 Passkey,不再询问密码。