WhatsApp은 전 세계 수십억 명의 사용자에게 서비스를 제공하는 세계 최대 규모의 메시지 플랫폼입니다. WhatsApp은 다양한 지역의 사용자가 비공개적이고 안정적이며 안전한 메시지를 통해 소통할 수 있는 기본 커뮤니케이션 도구입니다.
"가장 흥미로운 점은 WhatsApp이 미치는 영향의 규모입니다. WhatsApp의 패스키 기반 인증 설계 및 구현을 주도한 WhatsApp 등록 및 액세스팀의 Android 엔지니어 마얀크 마누자는 "WhatsApp을 조금만 개선해도 전 세계 수십억 명의 사용자에게 영향을 미칩니다"라고 말합니다.
이 정도 규모의 사용자를 위한 앱을 빌드하려면 다양한 네트워크 환경, 기기 기능, 디지털 리터러시 수준을 고려해야 합니다. WhatsApp은 패스키의 잠재력을 일찍이 인식하고 2023년에 패스키를 도입하기로 결정하여 이 기술을 통합한 최초의 주요 소비자 앱 중 하나가 되었습니다. WhatsApp은 패스키를 구현하여 계정 탈취 및 사용자 인증 정보 도용에 대한 강력한 보호 기능을 제공하면서도 사용자의 불편을 크게 줄이는 빠르고 피싱에 강한 옵션을 제공하고자 했습니다.
패스키 도입 결정
WhatsApp의 경우 사용자가 쉽게 연결을 유지하고 필요할 때 액세스 권한을 다시 얻을 수 있도록 하려면 여러 액세스 방법을 제공하는 것이 중요합니다. 패스키는 사용자에게 간소화된 원탭 로그인 환경을 제공하여 피싱 위험을 제거하고 OTP 메시지 전송이 일관되지 않은 지역에서도 안정적으로 작동합니다.
패스키는 공개 키-비공개 키 암호화를 활용하여 수동 입력을 생체 인식 또는 화면 잠금 인증으로 대체합니다. 이 워크플로는 사용자가 앱의 컨텍스트 내에서 계속 참여할 수 있도록 하는 통합된 하단 시트 인터페이스를 통해 프로세스를 한 번의 탭으로 줄여 로그인 속도를 크게 개선합니다. 패스키는 사용자에게 간소화된 로그인 환경을 제공하는 동시에 피싱 공격에 대한 강력한 기본 보호 기능을 제공하는 두 가지 이점을 제공합니다. 중요한 점은 기존 SMS OTP 전송이 불안정할 수 있는 지역에서도 안정적으로 작동한다는 것입니다.
강력하고 다양한 계정 액세스 방법을 사용하면 사용자가 가장 중요한 항목에 액세스하지 못하는 상황을 방지할 수 있습니다.
클라이언트 측 통합
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팀은 근본 원인을 조사하고 플랫폼 수준의 개선사항을 구현하여 생성 흐름을 더 원활하게 만들었습니다. 여기에서 인증 관리자와 관련된 일반적인 오류 코드 및 설명을 나열하고 원인에 관한 정보를 제공하는 종합 오류 가이드를 확인할 수 있습니다.
| 참고: 자세한 내용은 패스키 권장사항 블로그에서 패스키를 도입할 때 사용자 환경을 최적화하는 방법을 알아보세요. |
사용자 환경 개선
패스키는 2023년 초에 완전히 새로운 개념이었기 때문에 패스키 생성을 유도하는 확립된 패턴이 없었습니다. WhatsApp은 광범위한 A/B 테스트를 통해 가장 큰 이점을 얻을 수 있는 사용자를 타겟팅하는 맥락적 프레임워크를 개발했습니다. 이 전략은 지속적으로 발전했습니다. Android OS 흐름이 간소화된 단일 화면 환경으로 성숙해짐에 따라 WhatsApp은 중복되거나 혼란스러운 UI를 방지하기 위해 자체 프롬프트를 간소화했습니다.
서버 측 아키텍처 및 크로스 플랫폼 장애물
백엔드에서 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 백엔드팀의 아시시 초우다리는 직면한 주요 장애물을 다음과 같이 강조했습니다.
- 계정당 여러 패스키로 이전: WhatsApp의 기존 서버 로직은 사용자당 단일 사용자 인증 정보라는 가정과 깊이 연관되어 있었습니다. 현대의 멀티 디바이스 환경을 지원하기 위해 한도에 도달하면 가장 오래된 사용자 인증 정보를 지능적으로 삭제하는 제한된 목록 시스템을 설계했습니다. 절대적인 안정성을 보장하기 위해 이 주요 구조적 변화는 엄격한 실험을 통해 점진적으로 출시되었습니다.
- 사용자 인증 정보 수명 주기 균형 조정: 사용자 인증 정보 유효성을 관리하려면 세심한 관리가 필요했습니다. 사용자 인증 정보를 너무 적극적으로 무효화하면 불필요한 재등록이 발생하고, 너무 관대하면 오래된 사용자 인증 정보가 쌓이게 됩니다. WhatsApp은 균형 잡힌 수명 주기 상태를 구현하여 사용자를 불편하게 하지 않으면서도 강력한 보안을 유지하고, 비활성 패스키에 대한 자동 백그라운드 정리 기능을 보완하여 이 문제를 해결했습니다.
교차 기기 동기화 재검토
이 강력한 멀티 패스키 아키텍처를 통해 WhatsApp은 크로스 플랫폼 사용성을 완전히 재고할 수 있었습니다. 표준 WebAuthn 교차 기기 흐름에서는 한 기기에서 QR 코드를 스캔하고 다른 기기에서 블루투스를 통해 인증해야 합니다. 하지만 WhatsApp은 블루투스 종속 항목이 안정적이지 않으며 사용자가 새로운 QR 코드를 기존 WhatsApp 웹 연결 프로세스와 혼동하는 경우가 많다는 사실을 발견했습니다.
WhatsApp은 불안정한 교차 기기 전송 메커니즘을 강제하는 대신 사용자가 Android의 Google 비밀번호 관리자, iOS의 iCloud 키체인과 같은 여러 생태계에서 기본적으로 패스키를 보유할 수 있도록 합니다. 사용자가 새 플랫폼으로 이전할 때는 다음 로그인 시 새 패스키를 생성하기만 하면 됩니다. 이 접근 방식은 사용자에게 전혀 불편을 주지 않으며 새로운 멀티 패스키 서버 인프라 위에서 원활하게 작동합니다.
향후 계획
패스키를 출시한 이후 WhatsApp은 방대한 사용자층에서 자연스러운 도입이 활발하게 이루어지는 것을 확인했습니다. 이 앱은 기존의 여러 단계로 이루어진 로그인 프로세스를 하나의 원활한 생체 인식 동작으로 전환하여 사용자 경험을 크게 개선했습니다. 이러한 추진력을 바탕으로 WhatsApp은 이제 초기 로그인 외에도 패스키 유틸리티를 확장하여 패스키로 암호화된 백업과 같은 민감한 계정 작업에 원활한 인앱 재인증을 탐색하고 있습니다.
앞으로 WhatsApp은 플랫폼 파트너와 적극적으로 협력하여 마찰이 적은 사용자 인증 정보 생성 경로를 개척하고 있으며, 기기의 생체 인식 기능이 확장됨에 따라 진입 장벽이 자연스럽게 낮아질 것으로 예상하고 있습니다.
대규모로 빌드하는 개발자를 위한 권장사항
WhatsApp팀은 패스키를 대규모로 통합하려는 개발자를 위해 다음과 같은 중요한 권장사항을 공유합니다.
- 초기에 오류 분류에 투자: 다양한 인증 관리자 예외를 복구 가능한 상태와 종료 상태로 분류하고 각 시나리오에 대해 명확하고 원활한 대체 경로를 정의합니다.
- 자격 요건 유입경로 이해: 화면 잠금 유무, 생체 인식 하드웨어, Play 서비스 버전과 같은 기기 기능 확인을 계측하고, 흐름 중간에 실패하는 대신 자격 요건을 충족하지 않는 사용자를 사전에 제외하도록 흐름을 설계합니다.
- 대체 수단에 대비한 앱 준비: 지원되는 기기에서 패스키를 최적의 기본 인증 방법으로 사용하되, 기존 방법은 항상 신뢰할 수 있는 범용 대체 수단으로 유지합니다.
- OS 버전 단편화 계획: 패스키 동작은 운영체제에 따라 다를 수 있습니다. Android 13, 14, 15 이상에서 철저히 테스트하고 사용자 인증 정보 선택 UI에서 OEM별 변형을 고려합니다.
- thought 상황에 맞는 업셀링 및 교육: 보안 관련 작업 중에 패스키 생성을 자연스럽게 표시합니다. 사용자 채택을 유도하기 위해 이해하기 쉬운 언어로 가치 제안 (속도 및 보안)을 명확하게 강조합니다.
- 사전 모니터링: 생태계는 OS 업데이트마다 진화합니다. 지연 시간과 오류 패턴을 지속적으로 추적하여 변화하는 기기 환경에 한발 앞서 대응하세요.
패스키 및 인증 관리자 시작하기
통합 가이드와 공개 샘플 코드를 사용하여 Android에서 패스키와 인증 관리자를 직접 사용해 보세요.
궁금한 점이나 문제가 있으면 Android 사용자 인증 정보 Issue Tracker를 통해 공유해 주세요.
-
우수사례Uber는 Android Restore Credentials API를 활용하여 새 기기 로그인을 간소화함으로써 연간 수동 로그인 횟수를 400만 회 줄이고 사용자 유지율을 높일 것으로 예상합니다.
Niharika Arora, Tracy Agyemang • 읽는 데 5분 소요 -
우수사례X는 속보, 엔터테인먼트, 스포츠, 정치 등 다양한 주제를 다루는 소셜 미디어 앱으로, 전 세계 약 5억 명의 사용자가 실시간 해설과 함께 전체 스토리를 파악할 수 있도록 지원합니다.
Niharika Arora, Tracy Agyemang • 읽는 데 3분 소요 -
우수사례Grab은 동남아시아의 선도적인 슈퍼 앱으로, 하나의 앱을 통해 8개 국가의 수백만 명의 사용자에게 배달, 모빌리티, 금융 서비스를 제공합니다.
Ajesh Pai, Nguyen Van Minh, Tracy Agyemang • 읽는 데 5분 소요
Android 개발 관련 최신 정보를 이메일로 받아 보세요.