事例紹介

Zoho、パスキーと認証情報マネージャーの統合によりログイン速度が 6 倍に向上

所要時間: 10 分

Android デベロッパーは、セキュリティの強化、ユーザー エクスペリエンスの向上、開発の効率化を常に目指しています。セキュリティとシームレスなエクスペリエンスに重点を置いた包括的なクラウドベースのソフトウェア スイートである Zoho は、OneAuth Android アプリでパスキーを採用することで、大幅な改善を実現しました。

2024 年にパスキーを統合して以来、Zoho はログイン速度を以前の方法の最大 6 倍に向上させ、パスキーの導入率を前月比 31% 増加させました。

このケーススタディでは、認証の難しさを解消するために Zoho がパスキーと Android の Credential Manager API を採用した事例を紹介します。技術的な実装プロセスを詳しく説明し、影響力の大きい結果を強調します。

認証に関する課題の解決

Zoho では、ユーザー アカウントを保護するために複数の認証方法を組み合わせて使用しています。これには、Zoho 独自の多要素認証(MFA)ソリューションである Zoho OneAuth が含まれていました。このソリューションは、プッシュ通知、QR コード、時間ベースのワンタイム パスワード(TOTP)を使用したパスワード ベースの認証とパスワードレス認証の両方をサポートしていました。Zoho はフェデレーション ログインもサポートしており、Security Assertion Markup Language(SAML)やその他のサードパーティ ID プロバイダによる認証が可能でした。

課題

Zoho は、多くの組織と同様に、運用上の負担を軽減しながら、認証のセキュリティとユーザー エクスペリエンスの向上を目指していました。パスキーの導入につながった主な課題は次のとおりです。

  • セキュリティの脆弱性: 従来のパスワードベースの方法では、ユーザーがフィッシング攻撃やパスワード侵害を受けやすくなっていました。
  • ユーザーの不満: パスワードの使い回しにより、パスワードを忘れたり、不満を感じたり、面倒な復元プロセスに頼ることが増えたりしました。
  • 運用の非効率性: パスワードの再設定と MFA に関する問題の処理に、サポートのオーバーヘッドが大幅に発生していました。
  • スケーラビリティに関する懸念: ユーザーベースの拡大により、より安全で効率的な認証ソリューションが必要になりました。

パスキーへの移行の理由

Zoho のアプリにパスキーが実装されたのは、セキュリティとユーザー エクスペリエンスを大幅に向上させるパスワード不要のアプローチを提供することで、認証の課題に対処するためです。このソリューションは、フィッシングに強い認証、クロスデバイスで簡単にアクセスできるクラウド同期された認証情報、安全なログインのための生体認証(指紋認証や顔認識など)、PIN、パターンを活用することで、従来のパスワードに関連する脆弱性や不便さを軽減します。

Zoho は、認証情報マネージャーでパスキーを採用したことで、ログイン時間を最大 6 倍短縮し、パスワード関連のサポート費用を削減し、ユーザーの採用率が大幅に向上しました。4 か月でパスキー ログインが倍増し、前月比 31% の成長を達成しました。Zoho ユーザーは、より高速で簡単なログインとフィッシング対策に有効なセキュリティを利用できるようになりました。

ANDDM_Zoho_Quote_fabrice.png

Android での認証情報マネージャーによる実装

では、Zoho はどのようにしてこれらの成果を達成したのでしょうか?Android で認証を実装するための推奨 Jetpack ライブラリである Android の Credential Manager API を使用しました。

認証情報マネージャーは、さまざまな認証方法の処理を簡素化する Unified 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 に準拠していたため、パスキーのサポートへの移行をスムーズに行うことができました。これにより、サーバーサイドの実装プロセスが効率化されました。ただし、パスキー機能を完全に統合するには、特定の変更が必要でした。

最も大きな課題は、認証情報ストレージ システムの適応でした。Zoho の既存の認証方法では、主にパスワードと FIDO セキュリティ キーを多要素認証に使用していましたが、暗号公開鍵に基づくパスキーとは異なる保存方法が必要でした。この問題を解決するため、Zoho は WebAuthn プロトコルに従ってパスキーの公開鍵と関連データを安全に保存するために特別に設計された新しいデータベース スキーマを実装しました。この新しいシステムは、ユーザーとデバイスの情報に基づいて認証情報を検証して取得するルックアップ メカニズムとともに構築され、以前の認証方法との下位互換性が確保されています。

サーバーサイドのもう 1 つの調整は、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 エンコード(WebAuthn で認証情報 ID などのフィールドによく使用される)を標準の 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、Bluetooth など。認証システムがどのように通信するかを指定)のリストを 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 Directory 管理パネルで、管理者は組織全体でパスキーを必須のデフォルト認証方法として指定できます。このポリシーが有効になっている場合、従業員は次回ログイン時にパスキーを設定し、それ以降はパスキーを使用する必要があります。
  • ユーザー チョイス: 組織が特定のポリシーを適用しない場合、個々のユーザーが制御を維持します。ログイン時に、認証設定でパスキーまたはその他の構成済みオプションを選択して、希望する認証方法を選択できます。

エンドユーザーがパスキーを簡単に導入できるように、Zoho は次の機能を実装しました。

  • 簡単な設定: Zoho は、パスキーの設定を Zoho OneAuth モバイルアプリに直接統合しました(AndroidiOS の両方で利用可能)。ユーザーはアプリ内でいつでもパスキーを簡単に設定できるため、スムーズに移行できます。
  • 一貫したアクセス: パスキーのサポートは、主要なユーザー タッチポイント全体に実装され、ユーザーは次の方法でパスキーを使用して登録と認証を行うことができます。
  • Zoho OneAuth モバイルアプリ(Android、iOS)。
  • Zoho ウェブのアカウント ページ。

この方法により、管理者が義務付けた場合でも、ユーザーが選択した場合でも、パスキーの設定と使用のプロセスが、ユーザーがすでに使用しているプラットフォームにアクセス可能で統合されることが保証されました。パスキー認証のユーザー フローをスムーズに作成する方法について詳しくは、包括的なパスキーのユーザー エクスペリエンス ガイドをご覧ください。

開発速度と統合効率への影響

認証情報マネージャーは Unified API として、以前のログインフローと比較してデベロッパーの生産性向上にも貢献しました。これにより、複数の認証方法と API を個別に処理する複雑さが軽減され、統合が数か月から数週間に短縮され、実装エラーが減少しました。これにより、ログイン プロセスが合理化され、全体的な信頼性が向上しました。

認証情報マネージャーを使用してパスキーを実装することで、Zoho は全体的に大幅な改善を達成しました。

  • 大幅な速度向上
    • 従来のパスワード認証と比較して、ログイン速度が 2 倍に向上。
    • メールまたは SMS OTP 認証によるユーザー名または携帯電話番号と比較して、ログインが4 倍高速になります。
    • ユーザー名、パスワード、SMS または認証システム OTP 認証と比較して、6 倍高速なログイン。
  • サポート費用の削減
    • パスワード関連のサポート リクエスト(特にパスワードを忘れた場合)が減少します。
    • 既存のユーザーはパスキーで直接オンボーディングできるため、SMS ベースの 2FA に関連するコストを削減できます。
  • ユーザーの導入の促進とセキュリティの強化:
    • パスキーによるログインはわずか 4 か月で 2 倍に増加し、ユーザーの受け入れ度が高いことが示されています。
    • パスキーに移行したユーザーは、一般的なフィッシングやパスワード侵害の脅威から完全に保護されます。
    • 月ごとの導入率が 31% 増加しており、フィッシングや SIM スワップなどの脆弱性に対するセキュリティ強化の恩恵を毎日受けているユーザーが増えています。

推奨事項とベスト プラクティス

Android でパスキーを正常に実装するには、デベロッパーは次のベスト プラクティスを考慮する必要があります。

  • Android の Credential Manager API を活用する:
    • 認証情報マネージャーは認証情報の取得を簡素化し、デベロッパーの労力を軽減するとともに、統一された認証エクスペリエンスを実現します。
    • 単一のインターフェースで、パスワード、パスキー、フェデレーション ログイン フローを処理します。
  • 他の FIDO 認証ソリューションから移行する際にデータ エンコードの整合性を確保する:
    • FIDO セキュリティ キーなどの他の FIDO 認証ソリューションから移行する際は、すべての入力/出力の一貫した形式を処理するようにしてください。
  • エラー処理とロギングを最適化する:
    • シームレスなユーザー エクスペリエンスを実現するために、堅牢なエラー処理を実装します。
    • ローカライズされたエラー メッセージを提供し、詳細なログを使用して予期しない障害をデバッグして解決します。
  • パスキーの復元オプションについてユーザーに説明します。
    • ユーザーに復元オプションを事前に案内することで、ロックアウト シナリオを防止します。
  • 導入指標とユーザー フィードバックをモニタリングする:
    • ユーザー エンゲージメント、パスキーの導入率、ログイン成功率を追跡して、ユーザー エクスペリエンスの最適化を継続します。
    • さまざまな認証フローで A/B テストを実施して、コンバージョンとユーザー維持率を改善します。

パスキーと Android Credential Manager API を組み合わせることで、セキュリティを強化しながらユーザー エクスペリエンスを簡素化する、強力で統合された認証ソリューションが実現します。パスキーを使用すると、フィッシングのリスク、認証情報の盗難、不正アクセスを大幅に軽減できます。デベロッパーの皆様は、アプリでこのエクスペリエンスを試して、最も安全な認証をユーザーに提供することをおすすめします。

パスキーと認証情報マネージャーを使ってみる

公開サンプルコードを使用して、Android でパスキーと認証情報マネージャーを実際に試してみましょう。

ご質問や問題がございましたら、Android Credentials の Issue Tracker でお知らせください。

作成者:

続きを読む