우수사례

Zoho, 패스키 및 인증 관리자 통합으로 로그인 속도 6배 향상

전문 길이: 10분

Android 개발자는 보안을 강화하고, 사용자 환경을 개선하고, 개발을 간소화할 방법을 끊임없이 모색합니다. 보안과 원활한 환경에 중점을 둔 포괄적인 클라우드 기반 소프트웨어 제품군인 Zoho는 OneAuth Android 앱에 패스키를 채택하여 상당한 개선을 이루었습니다.

2024년에 패스키를 통합한 이후 Zoho는 이전 방법보다 최대 6배 빠른 로그인 속도패스키 채택률 31% 의 전월 대비 성장률을 달성했습니다.

이 우수사례에서는 인증 문제를 해결하기 위해 Zoho가 패스키와 Android의 Credential Manager API를 채택한 방법을 살펴봅니다. 기술 구현 프로세스를 자세히 설명하고 영향력 있는 결과를 강조합니다.

인증 문제 극복

Zoho는 여러 인증 방법을 조합하여 사용자 계정을 보호합니다. 여기에는 푸시 알림, QR 코드, 시간 기반 일회용 비밀번호 (TOTP)를 사용하여 비밀번호 기반 인증과 패스워드리스 인증을 모두 지원하는 자체 다중 인증 (MFA) 솔루션인 Zoho OneAuth가 포함되었습니다. Zoho는 보안 보장 마크업 언어 (SAML) 및 기타 서드 파티 ID 공급업체를 통한 인증을 허용하는 제휴 로그인도 지원했습니다.

도전과제

많은 조직과 마찬가지로 Zoho는 운영 부담을 줄이면서 인증 보안과 사용자 환경을 개선하고자 했습니다. 패스키 채택으로 이어진 주요 과제는 다음과 같습니다.

  • 보안 취약점: 기존의 비밀번호 기반 방법은 사용자를 피싱 공격과 비밀번호 유출에 취약하게 만들었습니다.
  • 사용자 불편: 비밀번호 피로로 인해 비밀번호를 잊어버리고, 불만을 느끼고, 번거로운 복구 프로세스에 대한 의존도가 높아졌습니다.
  • 운영 비효율성: 비밀번호 재설정 및 MFA 문제를 처리하는 데 상당한 지원 오버헤드가 발생했습니다.
  • 확장성 문제: 사용자 기반이 증가함에 따라 더 안전하고 효율적인 인증 솔루션이 필요했습니다.

패스키로 전환하는 이유는 무엇인가요?

패스키는 보안과 사용자 환경을 크게 개선하는 비밀번호 없는 방식을 제공하여 인증 문제를 해결하기 위해 Zoho 앱에 구현되었습니다. 이 솔루션은 피싱 방지 인증, 손쉬운 교차 기기 액세스를 위한 클라우드 동기화 사용자 인증 정보, 보안 로그인을 위한 생체 인식 (예: 지문 또는 얼굴 인식), PIN 또는 패턴을 활용하여 기존 비밀번호와 관련된 취약점과 불편함을 줄입니다.

인증 관리자를 통해 패스키를 도입한 Zoho는 로그인 시간을 최대 6배 단축하고 비밀번호 관련 지원 비용을 절감했으며, 강력한 사용자 채택을 확인했습니다. 4개월 만에 패스키 로그인이 두 배로 증가했으며 MoM 성장률은 31% 에 달했습니다. 이제 Zoho 사용자는 더 빠르고 쉬운 로그인과 피싱 방지 보안을 이용할 수 있습니다.

ANDDM_Zoho_Quote_fabrice.png

Android에서 인증 관리자를 사용한 구현

그렇다면 Zoho는 어떻게 이러한 성과를 달성했을까요? Android에서 인증을 구현하는 데 권장되는 Jetpack 라이브러리인 Android의 Credential Manager API를 사용했습니다.

인증 관리자는 다양한 인증 방법의 처리를 간소화하는 통합 API를 제공합니다. 비밀번호, 패스키, 제휴 로그인 (예: Google 계정으로 로그인)에 대해 여러 API를 사용하는 대신 단일 인터페이스를 사용합니다.

Zoho에서 패스키를 구현하려면 클라이언트 측과 서버 측 모두를 조정해야 했습니다. 다음은 패스키 생성, 로그인, 서버 측 구현 프로세스의 세부사항입니다.

패스키 생성

passkey.png

패스키를 생성하기 위해 앱은 먼저 Zoho 서버에서 구성 세부정보를 가져옵니다. 이 프로세스에는 지문이나 얼굴 인식과 같은 고유한 인증이 포함됩니다. requestJson 문자열로 형식이 지정된 이 확인 데이터는 앱에서 CreatePublicKeyCredentialRequest을 빌드하는 데 사용됩니다. 그러면 앱이 credentialManager.createCredential 메서드를 호출하여 사용자에게 기기 화면 잠금 (생체 인식, 지문, PIN 등)을 사용하여 인증하라는 메시지를 표시합니다.

사용자 확인이 완료되면 앱은 새로운 패스키 사용자 인증 정보 데이터를 수신하여 확인을 위해 Zoho 서버로 다시 전송하고, 서버는 사용자의 계정에 연결된 패스키 정보를 저장합니다. 프로세스 중 오류 또는 사용자 취소는 앱에서 포착하여 처리합니다.

로그인

Zoho Android 앱은 Zoho의 백엔드 서버에서 고유한 challenge를 비롯한 로그인 옵션을 요청하여 패스키 로그인 프로세스를 시작합니다. 그러면 앱은 이 데이터를 사용하여 GetCredentialRequest를 구성하여 패스키로 인증할 것임을 나타냅니다. 그런 다음 이 요청으로 Android CredentialManager.getCredential() API를 호출합니다. 이 작업은 표준화된 Android 시스템 인터페이스를 트리거하여 사용자에게 Zoho 계정을 선택하라는 메시지를 표시하고 (패스키가 여러 개 있는 경우) 기기에 구성된 화면 잠금 (지문, 얼굴 스캔 또는 PIN)을 사용하여 인증합니다. 인증에 성공하면 사용자 인증 정보 관리자가 서명된 어설션 (로그인 증명)을 Zoho 앱에 반환합니다. 앱은 이 어설션을 Zoho 서버에 전달하고, 서버는 사용자의 저장된 공개 키에 대해 서명을 확인하고 챌린지를 검증하여 보안 로그인 프로세스를 완료합니다.

서버 측 구현

Zoho는 백엔드 시스템이 이미 FIDO WebAuthn을 준수하여 패스키 지원으로 전환하는 데 도움이 되었으며, 서버 측 구현 프로세스를 간소화했습니다. 하지만 패스키 기능을 완전히 통합하려면 여전히 구체적인 수정이 필요했습니다.

가장 큰 과제는 사용자 인증 정보 저장 시스템을 적응시키는 것이었습니다. 주로 비밀번호와 FIDO 보안 키를 다단계 인증에 사용했던 Zoho의 기존 인증 방법은 암호화 공개 키를 기반으로 하는 패스키와 다른 저장 방식이 필요했습니다. 이 문제를 해결하기 위해 Zoho는 WebAuthn 프로토콜에 따라 패스키 공개 키와 관련 데이터를 안전하게 저장하도록 특별히 설계된 새로운 데이터베이스 스키마를 구현했습니다. 이 새로운 시스템은 사용자 및 기기 정보를 기반으로 인증 정보를 검증하고 검색하는 조회 메커니즘과 함께 구축되어 이전 인증 방법과의 하위 호환성을 보장합니다.

또 다른 서버 측 조정은 Android 기기의 요청을 처리하는 기능을 구현하는 것이었습니다. Android 앱에서 시작된 패스키 요청은 URI 기반 형식 (https://example.com/app)을 사용하는 표준 웹 출처와는 다른 고유한 출처 형식 (android:apk-key-hash:example)을 사용합니다. 이 형식을 올바르게 파싱하고, 앱의 서명 인증서의 SHA-256 지문 해시를 추출하고, 사전 등록된 목록에 대해 유효성을 검사하도록 서버 로직을 업데이트해야 했습니다. 이 확인 단계를 통해 인증 요청이 Zoho의 Android 앱에서 실제로 시작되었는지 확인하고 피싱 공격으로부터 보호할 수 있습니다.

이 코드 스니펫은 서버가 Android 전용 출처 형식을 확인하고 인증서 해시를 검증하는 방법을 보여줍니다.

  val origin: String = clientData.getString("origin")

if (origin.startsWith("android:apk-key-hash:")) { 
    val originSplit: List<String> = origin.split(":")
    if (originSplit.size > 3) {
               val androidOriginHashDecoded: ByteArray = Base64.getDecoder().decode(originSplit[3])

                if (!androidOriginHashDecoded.contentEquals(oneAuthSha256FingerPrint)) {
            throw IAMException(IAMErrorCode.WEBAUTH003)
        }
    } else {
        // Optional: Handle the case where the origin string is malformed    }
}

오류 처리

Zoho는 사용자 대상 오류와 개발자 대상 오류를 모두 관리하기 위해 강력한 오류 처리 메커니즘을 구현했습니다. 사용자가 패스키 설정을 수동으로 취소할 때 일반적인 오류인 CreateCredentialCancellationException가 표시되었습니다. Zoho는 잠재적인 UX 개선사항을 평가하기 위해 이 오류의 빈도를 추적했습니다. Android의 UX 권장사항에 따라 Zoho는 패스키에 대해 사용자를 더 잘 교육하고, 사용자가 패스키 사용 가능 여부를 인지하도록 하며, 후속 로그인 시도 중에 패스키 채택을 촉진하기 위한 조치를 취했습니다.

이 코드 예시에서는 가장 일반적인 패스키 생성 오류를 처리하는 Zoho의 접근 방식을 보여줍니다.

  private fun handleFailure(e: CreateCredentialException) {
    val msg = when (e) {
        is CreateCredentialCancellationException -> {
            Analytics.addAnalyticsEvent(eventProtocol: "PASSKEY_SETUP_CANCELLED", GROUP_NAME)
            Analytics.addNonFatalException(e)
            "The operation was canceled by the user."
        }
        is CreateCredentialInterruptedException -> {
            Analytics.addAnalyticsEvent(eventProtocol: "PASSKEY_SETUP_INTERRUPTED", GROUP_NAME)
            Analytics.addNonFatalException(e)
            "Passkey setup was interrupted. Please try again."
        }
        is CreateCredentialProviderConfigurationException -> {
            Analytics.addAnalyticsEvent(eventProtocol: "PASSKEY_PROVIDER_MISCONFIGURED", GROUP_NAME)
            Analytics.addNonFatalException(e)
            "Credential provider misconfigured. Contact support."
        }
        is CreateCredentialUnknownException -> {
            Analytics.addAnalyticsEvent(eventProtocol: "PASSKEY_SETUP_UNKNOWN_ERROR", GROUP_NAME)
            Analytics.addNonFatalException(e)
            "An unknown error occurred during Passkey setup."
        }
        is CreatePublicKeyCredentialDomException -> {
            Analytics.addAnalyticsEvent(eventProtocol: "PASSKEY_WEB_AUTHN_ERROR", GROUP_NAME)
            Analytics.addNonFatalException(e)
            "Passkey creation failed: ${e.domError}"
        }
        else -> {
            Analytics.addAnalyticsEvent(eventProtocol: "PASSKEY_SETUP_FAILED", GROUP_NAME)
            Analytics.addNonFatalException(e)
            "An unexpected error occurred. Please try again."
        }
    }
}

인트라넷 환경에서 패스키 테스트하기

Zoho는 비공개 인트라넷 환경에서 패스키를 테스트하는 데 초기 어려움을 겪었습니다. 패스키의 Google 비밀번호 관리자 확인 절차에서는 신뢰 당사자 (RP) 도메인을 검증하기 위해 공개 도메인 액세스가 필요합니다. 하지만 Zoho의 내부 테스트 환경에는 이러한 공개 인터넷 액세스가 없어 인증 절차가 실패하고 패스키 인증 테스트가 방해되었습니다. 이 문제를 해결하기 위해 Zoho는 애셋 링크 파일과 도메인 검증을 사용하여 임시 서버를 호스팅하는 공개적으로 액세스 가능한 테스트 환경을 만들었습니다.

Zoho의 공개 테스트 환경에서 사용되는 assetlinks.json 파일의 이 예는 비밀번호 유효성 검사를 위해 신뢰 당사자 도메인을 지정된 Android 앱과 연결하는 방법을 보여줍니다.

  [
    {
        "relation": [
            "delegate_permission/common.handle_all_urls",
            "delegate_permission/common.get_login_creds"
        ],
        "target": {
            "namespace": "android_app",
            "package_name": "com.zoho.accounts.oneauth",
            "sha256_cert_fingerprints": [
                "SHA_HEX_VALUE" 
            ]
        }
    }
]

기존 FIDO 서버와 통합

Android의 패스키 시스템은 최신 FIDO2 WebAuthn 표준을 활용합니다. 이 표준에서는 특정 JSON 형식의 요청이 필요하며, 이를 통해 네이티브 애플리케이션과 웹 플랫폼 간의 일관성을 유지할 수 있습니다. Android 패스키 지원을 사용 설정하기 위해 Zoho는 필수 FIDO2 JSON 구조를 준수하는 요청을 올바르게 생성하고 처리하도록 사소한 호환성 및 구조적 변경사항을 적용했습니다.

이 서버 업데이트에는 다음과 같은 몇 가지 구체적인 기술 조정이 포함되었습니다.

1. 인코딩 변환: 서버는 관련 데이터를 저장하기 전에 Base64 URL 인코딩 (일반적으로 사용자 인증 정보 ID와 같은 필드에 WebAuthn에서 사용됨)을 표준 Base64 인코딩으로 변환합니다. 아래 스니펫은 rawId가 표준 Base64로 인코딩되는 방법을 보여줍니다.

  // Convert rawId bytes to a standard Base64 encoded string for storage
val base64RawId: String = Base64.getEncoder().encodeToString(rawId.toByteArray())

2. 전송 목록 형식: 일관된 데이터 처리를 위해 서버 로직은 인증기가 통신하는 방식을 지정하는 USB, NFC, 블루투스와 같은 전송 메커니즘 목록을 JSON 배열로 처리합니다.

3. 클라이언트 데이터 정렬: Zoho팀에서 서버가 clientDataJson 필드를 인코딩하고 디코딩하는 방식을 조정했습니다. 이렇게 하면 데이터 구조가 Zoho의 기존 내부 API의 기대치와 정확하게 일치합니다. 아래 예는 서버에서 처리하기 전에 클라이언트 데이터에 적용되는 전환 로직의 일부를 보여줍니다.

  private fun convertForServer(type: String): String {
    val clientDataBytes = BaseEncoding.base64().decode(type)
    val clientDataJson = JSONObject(String(clientDataBytes, StandardCharsets.UTF_8))
    val clientJson = JSONObject()
    val challengeFromJson = clientDataJson.getString("challenge")
    // 'challenge' is a technical identifier/token, not localizable text.
    clientJson.put("challenge", BaseEncoding.base64Url()
        .encode(challengeFromJson.toByteArray(StandardCharsets.UTF_8))) 

    clientJson.put("origin", clientDataJson.getString("origin"))
    clientJson.put("type", clientDataJson.getString("type"))
    clientJson.put("androidPackageName", clientDataJson.getString("androidPackageName"))
    return BaseEncoding.base64().encode(clientJson.toString().toByteArray())
}

사용자 안내 및 인증 환경설정

Zoho의 패스키 전략의 핵심은 다양한 조직 요구사항에 맞게 유연성을 제공하면서 사용자 채택을 장려하는 것이었습니다. 이는 신중한 UI 디자인과 정책 제어를 통해 달성되었습니다.

Zoho는 조직마다 보안 요구사항이 다르다는 것을 인식했습니다. 이를 수용하기 위해 Zoho는 다음을 구현했습니다.

  • 관리자 시행: 관리자는 Zoho 디렉터리 관리 패널을 통해 전체 조직의 필수 기본 인증 방법으로 패스키를 지정할 수 있습니다. 이 정책이 사용 설정되면 직원은 다음에 로그인할 때 패스키를 설정하고 앞으로 이를 사용해야 합니다.
  • 사용자 선택: 조직에서 특정 정책을 시행하지 않으면 개별 사용자가 제어권을 유지합니다. 로그인 시 인증 설정을 통해 패스키 또는 기타 구성된 옵션 중에서 선호하는 인증 방법을 선택할 수 있습니다.

최종 사용자가 패스키를 매력적이고 간단하게 채택할 수 있도록 Zoho는 다음을 구현했습니다.

  • 간편한 설정: Zoho OneAuth 모바일 앱 (AndroidiOS 모두 지원)에 직접 통합된 Zoho 통합 패스키 설정 사용자는 언제든지 앱 내에서 패스키를 편리하게 구성하여 전환을 원활하게 할 수 있습니다.
  • 일관된 액세스: 패스키 지원은 주요 사용자 터치 포인트 전반에 구현되어 사용자가 다음을 통해 패스키를 사용하여 등록하고 인증할 수 있습니다.
  • Zoho OneAuth 모바일 앱 (Android 및 iOS)
  • Zoho 웹 계정 페이지

이 방법을 통해 관리자가 요구했는지 사용자가 선택했는지와 관계없이 패스키 설정 및 사용 프로세스가 이미 사용 중인 플랫폼에 액세스 가능하고 통합되도록 했습니다. 포괄적인 패스키 사용자 환경 가이드를 살펴보면 패스키 인증을 위한 원활한 사용자 흐름을 만드는 방법을 자세히 알아볼 수 있습니다.

개발자 속도 및 통합 효율성에 미치는 영향

또한 인증 관리자는 통합 API로서 이전 로그인 흐름에 비해 개발자 생산성을 개선하는 데도 도움이 되었습니다. 여러 인증 방법과 API를 별도로 처리하는 복잡성이 줄어들어 통합이 몇 달에서 몇 주로 단축되고 구현 오류가 줄었습니다. 이를 통해 로그인 프로세스가 간소화되고 전반적인 안정성이 개선되었습니다.

Zoho는 Credential Manager로 패스키를 구현하여 전반적으로 측정 가능한 수준으로 크게 개선했습니다.

  • 속도 대폭 향상
    • 기존 비밀번호 인증에 비해 로그인 속도가 2배 더 빠릅니다.
    • 이메일 또는 SMS OTP 인증을 사용하는 사용자 이름 또는 휴대전화 번호에 비해 로그인 속도가 4배 더 빠릅니다.
    • 사용자 이름, 비밀번호, SMS 또는 OTP 앱 OTP 인증에 비해 로그인 속도가 6배 빠릅니다.
  • 지원 비용 절감
    • 비밀번호 관련 지원 요청 감소(특히 비밀번호를 잊어버린 경우)
    • 기존 사용자가 패스키를 사용하여 직접 온보딩할 수 있으므로 SMS 기반 2FA와 관련된 비용이 절감됩니다.
  • 높은 사용자 채택률 및 향상된 보안:
    • 패스키 로그인이 4개월 만에 두 배로 증가하여 사용자의 수용도가 높음을 보여줍니다.
    • 패스키로 이전하는 사용자는 일반적인 피싱 및 비밀번호 유출 위협으로부터 완전히 보호됩니다.
    • 전월 대비 31% 의 채택률 증가를 기록하며, 더 많은 사용자가 피싱 및 SIM 스왑과 같은 취약점에 대한 보안 강화의 혜택을 매일 누리고 있습니다.

추천 및 권장사항

Android에서 패스키를 성공적으로 구현하려면 개발자는 다음 권장사항을 고려해야 합니다.

  • Android의 Credential Manager API 활용:
    • 인증 관리자는 인증 정보 검색을 간소화하여 개발자의 노력을 줄이고 통합된 인증 환경을 보장합니다.
    • 단일 인터페이스에서 비밀번호, 패스키, 제휴 로그인 흐름을 처리합니다.
  • 다른 FIDO 인증 솔루션에서 마이그레이션하는 동안 데이터 인코딩 일관성 보장:
    • FIDO 보안 키와 같은 다른 FIDO 인증 솔루션에서 이전하는 동안 모든 입력/출력의 형식이 일관되게 처리되는지 확인하세요.
  • 오류 처리 및 로깅 최적화:
    • 원활한 사용자 환경을 위해 강력한 오류 처리를 구현하세요.
    • 현지화된 오류 메시지를 제공하고 자세한 로그를 사용하여 예기치 않은 장애를 디버깅하고 해결합니다.
  • 사용자에게 패스키 복구 옵션 안내:
    • 복구 옵션에 관한 안내를 사용자에게 선제적으로 제공하여 계정 잠금 시나리오를 방지합니다.
  • 채택 측정항목 및 사용자 의견 모니터링:
    • 사용자 참여도, 패스키 채택률, 로그인 성공률을 추적하여 사용자 환경을 지속적으로 최적화하세요.
    • 다양한 인증 흐름에 대해 A/B 테스트를 실행하여 전환 및 회원 유지를 개선합니다.

패스키는 Android 인증 관리자 API와 결합되어 사용자 환경을 간소화하면서 보안을 강화하는 강력한 통합 인증 솔루션을 제공합니다. 패스키는 피싱 위험, 사용자 인증 정보 도용, 무단 액세스를 크게 줄여줍니다. 개발자는 앱에서 이 환경을 사용해 보고 사용자에게 가장 안전한 인증을 제공하는 것이 좋습니다.

패스키 및 인증 관리자 시작하기

공개 샘플 코드를 사용하여 Android에서 패스키와 Credential Manager를 직접 사용해 보세요.

질문이나 문제가 있는 경우 Android 사용자 인증 정보 문제 추적기를 통해 공유해 주세요.

작성자:

계속 읽기