WhatsApp 是全球最大的即时通讯平台,为全球数十亿用户提供服务。它是不同地区用户的默认通讯工具,通过私密、可靠且安全的即时通讯服务连接用户。
“最让我兴奋的是 WhatsApp 的巨大影响力。WhatsApp 注册和访问团队的 Android 工程师 Mayank Manuja 负责领导 WhatsApp 基于通行密钥的身份验证的设计和实现,他表示:“即使是对 WhatsApp 进行微小的改进,也会影响全球数十亿用户。”
为如此庞大的受众群体打造产品,需要应对各种各样的网络条件、设备功能和数字素养水平。WhatsApp 敏锐地察觉到了通行密钥的潜力,于 2023 年承诺采用通行密钥,成为首批集成该技术的主要消费者应用之一。通过实现通行密钥,WhatsApp 旨在提供一种快速、防钓鱼式攻击的选项,在显著减少用户阻力的同时,提供强大的保护,防止账号入侵和凭证盗用。
决定采用通行密钥
对于 WhatsApp 而言,提供多种访问方式是让用户更轻松地保持联系并在需要时重新获得访问权限的关键。通行密钥可为用户提供顺畅的一键登录体验,不仅能消除钓鱼式攻击风险,而且即使在动态密码消息传送不稳定的地区也能可靠运行。
在底层,通行密钥利用公钥-私钥加密技术,将手动输入替换为生物识别或屏幕锁定身份验证。此工作流程通过统一的底部动作条界面,将登录过程简化为一键操作,从而大幅提升登录速度,让用户在应用上下文中保持用户互动度。通行密钥具有双重优势:既能为用户提供顺畅的登录体验,又能提供强大的原生保护,防范钓鱼式攻击。至关重要的是,即使在传统短信动态密码传送可能不稳定的地区,它们也能可靠地运行。
提供可靠且多样的账号访问方式,可确保用户始终能够访问对其最重要的内容。
客户端集成
从 WhatsApp 开发者的角度来看,Credential Manager API 提供了一个简洁、统一的界面,消除了底层凭据提供程序的复杂性。在规划好初始集成流程后,API 接口变得简单明了,凭证创建和检索遵循明确定义的请求和响应模式。如需查看实现指南,请参阅 Android 开发者文档。
虽然理想路径从一开始就运行良好,但面对来自不同 OEM、使用多个 Android 版本和各种设备配置(例如仅限 PIN 码与生物识别,或 Android 13 与 Android 14+)的多样化用户群,我们遇到了前所未有的极端情况。这些问题包括用户未设置屏幕锁定、异常类型不符合预期、Play 服务版本过旧,以及凭据提供程序行为不一致。
为了克服这些障碍,WhatsApp 和 Google 团队密切合作,共同应对了多项挑战:
- 优化凭证查找流程:初始查找流程存在严重的延迟问题,尤其是对于尚未创建通行密钥的用户。由于大多数 WhatsApp 用户在早期阶段都属于这一类,因此几乎每次登录都会出现明显的延迟。通过共同检测调用路径并找出瓶颈,WhatsApp 显著加快了这一过程,实现了最终惠及整个 Android 生态系统的性能提升。
- 处理瞬态:WhatsApp 构建了一个全面的错误处理层,用于应对设备特有的障碍,例如密码管理工具不可用、未配置屏幕锁定、间歇性连接问题、硬件不兼容、Play 服务过时,并将异常分为可恢复状态和终端状态。这样可以实现优雅降级,如果通行密钥流程无法完成,系统会安全地回退到传统身份验证,而不会让用户陷入无法登录的状态。
- 解决操作系统特有的异常问题:遥测数据显示,在某些 Android 13 设备上,用户会遇到 GetPublicKeyCredentialDomException(无法解密凭证)等设备特有的障碍;在 Android 14 上创建通行密钥时,用户会遇到 CreatePublicKeyCredentialDomException(无法获取同步账号)等障碍。针对这些问题,Google 和 WhatsApp 团队调查了根本原因,并实施了平台级改进,以确保创建流程更加顺畅。您可以点击此处查看全面的错误指南,其中列出了与 Credential Manager 相关的常见错误代码和说明,并提供了一些相应的原因。
| 注意:如需更多指导,请参阅通行密钥最佳实践博客,了解如何在采用通行密钥时优化用户体验。 |
优化用户体验
由于通行密钥在 2023 年初还是一个全新的概念,因此当时并没有成熟的模式来提示用户创建通行密钥。通过广泛的 A/B 测试,WhatsApp 开发了一个情境框架,专门针对最能从中受益的用户。这一策略不断演进:随着 Android OS 流程逐渐成熟,演变为流畅的单屏体验,WhatsApp 也简化了其提示,以避免出现冗余或令人困惑的界面。
服务器端架构和跨平台障碍
在后端,WhatsApp 的服务器实现了标准的 WebAuthn/FIDO2 流程。后端是用 Erlang 编写的,并通过原生接口调用 Rust webauthn-rs 库。此 Rust 库负责处理签名验证和凭证解析,使内部代码能够专注于编排、存储和产品规则(例如资格、速率限制和凭证生命周期)。
服务器架构通过四个主要入口点来协调这些核心仪式,这些入口点分为注册和身份验证的开始和完成序列:
1. 通行密钥注册
此序列负责向客户端发出创建选项,在客户端确认成功创建后验证认证,并安全地保留凭据。
Erlang:开始注册
begin_registration(UserId) ->
Existing = list_credentials(UserId),
%% reuse the existing user handle, or mint a new one
{UserHandle, IsNew} = user_handle(Existing),
%% returns the client creation options and the server-side challenge state
#{client_safe := CreationOptions, server_only := ChallengeState} =
webauthn:start_registration(UserId, UserHandle, rp_config()),
%% excludeCredentials: the user's existing credential IDs, so the device won't re-enroll one
Options = with_exclude_credentials(CreationOptions, credential_ids(Existing)),
store_challenge(UserId, ChallengeState), %% short TTL
IsNew andalso reserve_user_handle(UserId, UserHandle),
Options.- 识别用户:服务器首先会检查是否有任何现有凭证,以便重复使用现有用户句柄或生成新句柄。
- 生成选项和质询:它会调用 WebAuthn 库,为客户端生成创建选项,并为服务器生成安全的质询状态。
- 防止重复:它会明确排除用户现有的凭证 ID,以免设备意外重新注册已注册的通行密钥。
- 存储 challenge:服务器会暂时存储 challenge,并设置较短的存留时间 (TTL),然后将 options 发送回客户端设备。
Erlang:完成注册
finish_registration(UserId, Attestation) ->
ChallengeState = get_challenge(UserId), %% must exist and be unexpired
#{credential_id := CredId, public_key := PubKey} =
webauthn:finish_registration(Attestation, ChallengeState, rp_config()),
ok = index_credential(CredId, UserId), %% map credential_id -> account
case multi_passkey_enabled(UserId) of
true -> add_credential(UserId, CredId, PubKey); %% append (oldest evicted past the cap)
false -> replace_credential(UserId, CredId, PubKey) %% single-passkey mode
end,
notify_client(UserId, {passkey_created, CredId}),
ok.- 检索验证请求:服务器检索存储的验证请求,确保其仍然存在且未过期。
- 验证证明:它会将客户端的响应(证明)和质询传递给 WebAuthn 库,以验证请求并提取新的凭证 ID 和公钥。
- 为凭证建立索引:新的凭证 ID 会直接映射到用户的账号,以便日后快速查找。
- 保存和管理限制:根据是否启用了多通行密钥功能,服务器会将新凭证附加到用户的列表中(如果达到上限,则会逐出最旧的凭证),或者在单通行密钥模式下替换现有凭证。
2. 凭证身份验证
与创建过程类似,应用服务器通过编排登录序列来处理身份验证流程。这包括在客户端成功通过身份验证后验证断言,以及在 WebAuthn 发出需要刷新的信号时动态更新存储的凭证。
Erlang:开始身份验证
begin_authentication(UserId) ->
Credentials = list_valid_credentials(UserId),
#{client_safe := RequestOptions, server_only := ChallengeState} =
webauthn:start_authentication(Credentials, rp_config()),
store_challenge(UserId, ChallengeState), %% short TTL
RequestOptions.- 提取有效凭证:服务器会查找与用户关联的所有当前有效凭证。
- 生成质询:它使用这些凭证为客户端构建请求选项,并生成新的服务器端质询。
- 存储和返回:与注册过程一样,系统会暂时保存验证请求,并将请求选项传递给客户端应用。
Erlang:完成身份验证
finish_authentication(UserId, Assertion) ->
ChallengeState = get_challenge(UserId),
Credentials = list_valid_credentials(UserId),
case webauthn:finish_authentication(Credentials, Assertion, ChallengeState) of
#{user_verified := true, credential_id := CredId, needs_update := NeedsUpdate} = Result ->
%% webauthn tells us when the stored credential should be refreshed
NeedsUpdate andalso refresh_credential(UserId, CredId, Result),
mark_credential_used(UserId, CredId),
{ok, CredId};
_ ->
{error, not_allowed}
end.- 验证断言:服务器检索存储的质询和有效凭据,然后要求 WebAuthn 库验证客户端的断言。
- 根据需要刷新:如果用户成功通过验证,服务器会检查 needs_update 标志。WebAuthn 库会使用此标志来指示是否需要在服务器上刷新存储的凭证状态。
- 完成:服务器将凭证标记为已使用,并成功完成登录流程。
如需详细了解服务器注册,请参阅此处的集成指南。
高级架构注意事项
在服务器上大规模实现通行密钥面临着独特的挑战,尤其是在账号架构和设备同步方面。WhatsApp 后端团队的 Ashish Choudhary 重点介绍了他们面临的主要障碍:
- 迁移到每个账号多个通行密钥:WhatsApp 的旧版服务器逻辑与每个用户一个凭据的假设深度交织。为了支持现代多设备环境,他们设计了一个有界列表系统,该系统会在达到上限后智能地逐出最旧的凭据。为确保绝对稳定,我们通过严格的实验逐步推出了这一重大结构性转变。
- 平衡凭证生命周期:管理凭证有效期需要谨慎处理。如果凭证失效过于频繁,会导致不必要的重新注册;而如果过于宽松,则会导致过时凭证堆积。为了解决这个问题,WhatsApp 实现了平衡的生命周期状态,既能保持严格的安全性,又不会让用户感到沮丧,同时还辅以针对不活跃通行密钥的自动后台清理。
重新思考跨设备同步
这种强大的多通行密钥架构还让 WhatsApp 能够全面重新思考跨平台易用性。标准的 WebAuthn 跨设备流程要求在一台设备上扫描二维码,并在另一台设备上通过蓝牙进行身份验证。不过,WhatsApp 发现蓝牙依赖项并不可靠,而且用户经常会将新的二维码与现有的 WhatsApp 网页版关联流程混淆。
WhatsApp 没有强制采用不稳定的跨设备传输机制,而是允许用户在多个生态系统中原生保存通行密钥,例如 Android 上的 Google 密码管理工具和 iOS 上的 iCloud 钥匙串。当用户迁移到新平台时,只需在下次登录时生成新的通行密钥即可。这种方法对用户来说完全没有阻碍,并且可以在新的多通行密钥服务器基础架构上无缝运行。
展望未来
自推出通行密钥以来,WhatsApp 见证了其庞大用户群的自然采用率。通过将传统的多步骤登录流程转变为单一、顺畅的生物识别手势,该应用显著提升了用户体验。在此基础上,WhatsApp 正在将通行密钥的应用范围扩展到初始登录之外,探索在应用内无缝重新进行身份验证,以便执行通行密钥加密备份等敏感账号操作。
展望未来,WhatsApp 正在积极与平台合作伙伴合作,率先推出更顺畅的凭证创建路径,并预计随着设备生物识别功能的扩展,准入门槛自然会降低。
针对大规模开发者的建议
对于准备大规模集成通行密钥的开发者,WhatsApp 团队分享了以下重要建议:
- 尽早建立错误分类:将各种 Credential Manager 异常分类为可恢复状态和终止状态,并为每种情况定义清晰、优雅的回退路径。
- 了解资格条件漏斗:实施设备功能检查(例如屏幕锁定状态、生物识别硬件和 Play 服务版本),并设计流程以主动排除不符合条件的用户,而不是在流程中途失败。
- 为应用准备好后备方案:在支持通行密钥的设备上,将通行密钥作为首选的主要身份验证方法,但始终保留传统方法作为可靠、通用的后备方案。
- 针对操作系统版本碎片化做好规划:通行密钥的行为可能因操作系统而异。在 Android 13、14 和 15 及更高版本上进行全面测试,并考虑 OEM 特有的凭据选择界面差异。
- 在适当情境下追加销售并进行宣传:在执行与安全相关的操作时,自然地呈现通行密钥创建选项。使用通俗易懂的语言,明确强调价值主张(速度和安全性),以提高用户采用率。
- 主动监控:生态系统会随着每次操作系统更新而不断演变。持续跟踪延迟时间和错误模式,以便及时应对不断变化的设备环境。
通行密钥和 Credential Manager 使用入门
借助我们的集成指南和公开示例代码,在 Android 上亲身体验通行密钥和 Credential Manager。
如果您有任何疑问或问题,可以通过 Android 凭据问题跟踪器与我们分享。
-
案例研究Uber 利用 Android Restore Credentials API 简化了新设备登录流程,预计每年可减少 400 万次手动登录,并提高用户留存率。
Niharika Arora, Tracy Agyemang • 阅读用时:5 分钟 -
案例研究从重大新闻和娱乐到体育和政治,X 是一款社交媒体应用,旨在帮助全球近 5 亿用户了解完整故事和所有实时评论。
Niharika Arora, Tracy Agyemang • 阅读用时:3 分钟 -
案例研究Grab 是东南亚领先的超级应用,通过单一应用为八个国家/地区的数百万用户提供配送、出行和金融服务。
Ajesh Pai, Nguyen Van Minh, Tracy Agyemang • 阅读用时:5 分钟
每周通过电子邮件接收最新的 Android 开发洞见。