WhatsApp 是全球最大的訊息平台,為全球數十億使用者提供服務。WhatsApp 是不同地區使用者預設的通訊工具,透過私密、可靠且安全的訊息服務,將使用者連結在一起。
「最讓我興奮的是 WhatsApp 的影響力。WhatsApp 註冊與存取團隊的 Android 工程師 Mayank Manuja 負責主導 WhatsApp 密碼金鑰驗證機制的設計與實作,他表示:「WhatsApp 只要有小幅改善,就能造福全球數十億名使用者。」
為如此龐大的客群開發產品,必須考量各種網路條件、裝置功能和數位素養程度。WhatsApp 很快就意識到密碼金鑰的潛力,並在 2023 年採用這項技術,成為首批整合密碼金鑰的主要消費者應用程式之一。WhatsApp 導入密碼金鑰,旨在提供快速、可防範網路釣魚的選項,大幅減少使用者操作阻力,同時提供強大的防護機制,防止帳戶入侵和憑證竊取。
決定採用密碼金鑰
對 WhatsApp 來說,提供多種存取方式是關鍵,可讓使用者更輕鬆地保持聯繫,並在需要時重新取得存取權。密碼金鑰可讓使用者一鍵登入,不僅能消除網路釣魚風險,即使在動態密碼訊息傳送不穩定的地區,也能可靠運作。
密碼金鑰採用公私鑰密碼學技術,以生物辨識或螢幕鎖定驗證取代手動輸入。這個工作流程採用統一的底部功能表介面,使用者只要輕觸一下就能登入,大幅加快登入速度,並讓使用者持續參與應用程式。這項功能有兩大優點:使用者可享有流暢的登入體驗,同時獲得強大的原生防護,防範網路釣魚攻擊。更重要的是,即使在傳統簡訊動態密碼傳送不穩定的地區,這項功能也能穩定運作。
提供多種可靠的帳戶存取方式,可確保使用者不會無法存取重要內容。
用戶端整合
從 WhatsApp 開發人員的角度來看,Credential Manager API 提供簡潔、統一的介面,可免除底層憑證提供者的複雜性。初步整合流程規劃完成後,API 介面就變得簡單明瞭,憑證建立和擷取作業也遵循明確定義的要求和回應模式。如需導入指南,請參閱 Android 開發人員說明文件。
雖然滿意路徑一開始就運作良好,但由於使用者族群多元,涵蓋不同原始設備製造商 (OEM)、多個 Android 版本和各種裝置設定 (例如僅限 PIN 碼與生物辨識,或 Android 13 與 14 以上版本),因此出現了前所未有的極端案例。包括使用者未設定螢幕鎖定、未預期的例外狀況類型、Play 服務版本過舊,以及憑證提供者行為不一致。
為克服這些難題,WhatsApp 和 Google 團隊密切合作,解決了多項挑戰:
- 最佳化憑證查詢流程:初始查詢流程的延遲時間過長,尤其是尚未建立密碼金鑰的使用者。由於大多數 WhatsApp 使用者在早期階段都屬於這個類別,因此幾乎每次登入都會明顯延遲。WhatsApp 透過檢測呼叫路徑並找出瓶頸,大幅加快程序,提升效能,最終造福整個 Android 生態系統。
- 處理暫時狀態:WhatsApp 建構了全面的錯誤處理層,可解決裝置特有的問題,例如密碼管理工具可用性、未設定螢幕鎖定、間歇性連線問題、不相容的硬體、Play 服務過時,以及將例外狀況分類為可復原和終端狀態。這可確保系統在密碼金鑰流程無法完成時,能安全地退回傳統驗證方式,不會讓使用者卡在錯誤狀態。
- 解決特定 OS 異常狀況:遙測資料顯示,某些 Android 13 裝置會出現 GetPublicKeyCredentialDomException (無法解密憑證) 等裝置專屬問題,Android 14 裝置則會在建立密碼金鑰時出現 CreatePublicKeyCredentialDomException (無法取得同步帳戶)。Google 和 WhatsApp 團隊調查根本原因後,實施平台層級改善措施,確保建立流程更順暢。您可以參閱這份完整的錯誤指南,其中列出與 Credential Manager 相關的常見錯誤代碼和說明,並提供造成這些錯誤的部分原因。
| 注意:如需進一步指引,請參閱密碼金鑰最佳做法網誌,瞭解如何在採用密碼金鑰時,提供最佳使用者體驗。 |
改善使用者體驗
由於密碼金鑰在 2023 年初是全新概念,因此沒有建立密碼金鑰的既定模式。WhatsApp 進行了廣泛的 A/B 測試,開發出情境架構,鎖定最能受惠的使用者。這項策略持續演進:隨著 Android 作業系統流程發展成精簡的單一畫面體驗,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,避免裝置不小心重新註冊已註冊的密碼金鑰。
- 儲存驗證碼:伺服器會暫時儲存驗證碼,並設定短暫的存留時間 (TTL),然後將選項傳回用戶端裝置。
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 跨裝置流程需要在一台裝置上掃描 QR code,並在另一台裝置上透過藍牙進行驗證。不過,WhatsApp 發現藍牙依附元件不穩定,且使用者經常混淆新的 QR code 和現有的 WhatsApp 網頁連結程序。
WhatsApp 不會強制使用不穩定的跨裝置傳輸機制,而是允許使用者在多個生態系統中原生保留密碼金鑰,例如 Android 上的 Google 密碼管理工具和 iOS 上的 iCloud 鑰匙圈。使用者遷移至新平台時,只要在下次登入時產生新的密碼金鑰即可。這種做法完全不會對使用者造成干擾,而且能在新的多密碼金鑰伺服器基礎架構上順暢運作。
未來計畫
自推出密碼金鑰以來,WhatsApp 龐大的使用者族群中,有許多人自然而然地採用這項功能。這款應用程式將傳統的多步驟登入程序,轉換為單一、流暢的生物辨識手勢,大幅改善使用者體驗。WhatsApp 乘勝追擊,將密碼金鑰的用途擴展到初始登入以外的範圍,探索如何讓使用者在應用程式中順暢重新驗證,以便執行敏感帳戶操作,例如使用密碼金鑰加密備份。
展望未來,WhatsApp 正積極與平台合作夥伴合作,率先推出更流暢的憑證建立路徑,預期隨著裝置生物辨識功能擴展,進入門檻自然會降低。
給大規模建構應用程式開發人員的建議
如果開發人員準備大規模整合密碼金鑰,WhatsApp 團隊會提供下列重要建議:
- 及早建立錯誤分類:將各種 Credential Manager 例外狀況分類為可復原或終止狀態,並為每種情況定義清楚、流暢的備用路徑。
- 瞭解資格漏斗:檢測裝置功能,例如螢幕鎖定、生物特徵辨識硬體和 Play 服務版本,並設計流程主動排除不符合資格的使用者,而不是在流程中失敗。
- 為應用程式準備備用方案:在支援的裝置上,將密碼金鑰設為主要驗證方式,但一律保留傳統方法,做為可靠、通用的備用方案。
- 規劃 OS 版本分散問題:密碼金鑰行為可能因作業系統而異。在 Android 13、14 和 15 以上版本進行全面測試,並考量 OEM 專屬的憑證選取 UI 變化。
- 適時推廣並說明:在使用者執行安全性相關操作時,自然地引導他們建立密碼金鑰。使用淺顯易懂的語言,清楚強調價值主張 (速度和安全性),吸引使用者採用。
- 主動監控:生態系統會隨著每次 OS 更新而演變。持續追蹤延遲時間和錯誤模式,因應不斷變化的裝置環境。
開始使用密碼金鑰和 Credential Manager
請參閱整合指南和公開程式碼範例,瞭解如何在 Android 裝置上使用密碼金鑰和 Credential Manager。
如有任何問題或疑問,歡迎透過 Android Credentials Issue Tracker 與我們聯絡。
-
個案研究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 開發洞察資訊。