一意の識別子に関するベスト プラクティス

このドキュメントでは、ユースケースに応じて適切なアプリ識別子を選択するための参考情報を紹介しています。

Android における権限全般については、権限の概要をご覧ください。Android の権限に関するベスト プラクティスについては、アプリの権限に関するおすすめの設定をご覧ください。

Android の識別子の使用に関するベスト プラクティス

Android の識別子を使用する際は、次のベスト プラクティスに沿って行ってください。

  1. ハードウェア ID を使用しない。 ほとんどのユースケースにおいて、必要な機能を制限することなく、SSAID(Android ID)や IMEI といったハードウェア ID を使用しないように選択することができます。

    Android 10(API レベル 29)以降、再設定不可能なデバイス ID にアプリがアクセスするには、デバイス オーナー アプリやプロファイル オーナー アプリであるか、特別な携帯通信会社パーミッションを持っているか、READ_PRIVILEGED_PHONE_STATE 特権パーミッションを持っている必要があります。

  2. ユーザー プロファイル作成や広告のユースケースでは広告 ID だけを使用する。 広告 ID を使用する際は、必ず広告のトラッキングに関してユーザーが選択した設定を優先してください。また、広告 ID と個人情報(PII)をリンクしたり、広告 ID のリセットをブリッジしたりしないようにしてください。

  3. 不正決済防止と電話機能のユースケースを除き、他のすべてのユースケースでは、可能な限りインスタンス ID または非公開環境に保存した GUID を使用する。 非広告のユースケースでは、ほとんどの場合、インスタンス ID または GUID だけで十分なはずです。

  4. プライバシーに関するリスクを最小限に抑えるため、ユースケースに適した API を使用する。 価値の高いコンテンツの保護には DRM API を、不正行為防止には SafetyNet API を使用します。SafetyNet API は、プライバシー リスクを発生させずにデバイスが正規品であるかどうかを判定できる最も簡単な手段です。

以下のセクションでは、Android アプリの開発時に上記のルールをどのように適用するのかについて詳細に説明します。

広告 ID を使用する

広告 ID は、ユーザーがリセットできる識別子であり、広告のユースケースに適しています。ただし、広告 ID の使用にあたっては注意点がいくつかあります。

広告 ID のリセットに関しては必ずユーザーの希望を優先する。ユーザーの同意なく、ユーザーによるリセットをブリッジする(別の ID やフィンガープリントを使用して新しい広告 ID とリンクする)ことはしないでください。Google Play デベロッパー コンテンツ ポリシーに次のような規定があります。

「ユーザーの明示的な同意なしに、リセットの際に新しい広告 ID を以前の広告 ID や以前の広告 ID からのデータにリンクしてはなりません。」

関連付けられているパーソナライズド広告のフラグを優先する。 広告 ID の設定では、その ID に関連付けられているトラッキングの頻度や範囲をユーザーが制限できるようになっています。必ず AdvertisingIdClient.Info.isLimitAdTrackingEnabled() メソッドを使用して、ユーザーが選択した設定が無視されないようにしてください。Google Play デベロッパー コンテンツ ポリシーに次のような規定があります。

「...ユーザーが指定した [インタレスト ベース広告をオプトアウト] または [広告のパーソナライズをオプトアウトする] の設定を遵守する必要があります。ユーザーがこの設定を有効にした場合は、広告目的でユーザーのプロフィールを作成したり、ユーザーをパーソナライズド広告のターゲットに設定したりするために広告 ID を使用してはなりません。ただし、コンテンツ ターゲット広告、フリークエンシー キャップ、コンバージョン トラッキング、レポート、セキュリティや不正行為の検出などに使用することはできます。」

広告 ID の使用に関して、使用している SDK に適用されるプライバシー ポリシーやセキュリティ ポリシーに注意する。たとえば、Google アナリティクス SDK の enableAdvertisingIdCollection() メソッドに true を渡す場合、必ず、適用されるすべてのアナリティクス SDK ポリシーを確認し遵守するようにしてください。

また、Google Play デベロッパー コンテンツ ポリシーは、広告 ID を「個人情報にリンクしたり、永続的なデバイス ID(SSAID、MAC アドレス、IMEI など)に関連付けたりしない」ことを求めています。

たとえば、情報を収集してデータベース テーブルの次の列に入力しようとしている場合を考えてみましょう。

TABLE-01
timestamp ad_id account_id clickid
TABLE-02
account_id name dob country

この例の場合、両方のテーブルの account_id 列を使用することで、ad_id 列を PII に関連付けることができます。ただし、ユーザーの明示的なパーミッションなくこの処理を行うと、Google Play デベロッパー コンテンツ ポリシーに違反することになります。

広告主 ID と PII の関係付けは、この例ほど単純ではない場合もあります。PII と広告 ID をキーとするテーブルの両方に「疑似識別子」が存在する可能性があり、そのことによって問題が発生することもあります。たとえば、TABLE-01 と TABLE-02 を次のように変更するとします。

TABLE-01
timestamp ad_id clickid dev_model
TABLE-02
timestamp demo account_id dev_model name

この場合は、クリック イベントがごくまれにしか発生しなかったときに、イベントのタイムスタンプとデバイスモデルを使用して TABLE-01 の広告主 ID と TABLE-02 の PII が関連付けられる可能性があります。

多くの場合、データセットにこのような疑似識別子が一切存在しないようにすることは困難ですが、関連付けが発生する明らかなリスクがある場合については、できる限り一意のデータを一般化することによってリスクを回避できます。この例では、タイムスタンプの精度を低下させると、同じモデルの複数のデバイスが同じタイムスタンプで存在することになります。

他にも次のような解決策が考えられます。

  • PII と広告 ID を明示的にリンクするようなテーブル設計は行わない。上記の最初の例でいえば、TABLE-01 に account_id 列を含めないようにします。

  • 広告 ID をキーとするデータと PII の両方にアクセスできるユーザーや役割のアクセス制御リストを分離してモニタリングする。たとえば、テーブル間を結合するなどして、両方のソースに同時にアクセスできる権限を厳格に管理、監査することで、広告 ID と PII の間の関連付けが発生するリスクを軽減できます。一般に、アクセス制御では次のことを行います。

    1. 広告主 ID をキーとするデータと PII の各アクセス制御リスト(ACL)を互いに関連付けられていない状態に維持し、両方の ACL に入っている人や役割の数を最小限に抑える。
    2. アクセスログ作成と監査の機能を実装し、ルールの例外を検知して管理する。

責任を持って広告 ID を扱う方法については、AdvertisingIdClient API リファレンスをご覧ください。

インスタンス ID や GUID を使用する

デバイス上で実行されているアプリのインスタンスを特定する方法としては、インスタンス ID が最もわかりやすく、広告以外の大多数のユースケースではこの方法をおすすめします。インスタンス ID には、割り当てられたアプリ インスタンスのみがアクセスできます。アプリがインストールされている間のみ存続するため、インスタンス ID は比較的簡単にリセットできます。

そのため、インスタンス ID は、リセットできないデバイスレベルのハードウェア ID に比べて、優れたプライバシー特性を備えています。詳細については、FirebaseInstanceId API リファレンスをご覧ください。

インスタンス ID を使用することが実用的でないケースでは、カスタムの Globally-Unique ID(GUID)を使用してアプリ インスタンスを一意に識別することもできます。最も簡単なのは、次のコードを使用して独自の GUID を作成する方法です。

Kotlin

    var uniqueID = UUID.randomUUID().toString()
    

Java

    String uniqueID = UUID.randomUUID().toString();
    

この識別子はグローバルに一意であるため、特定のアプリ インスタンスの特定に使用できます。複数のアプリ間での識別子の関連付けによる問題を避けるため、GUID は外部(共有)ストレージではなく内部のストレージに保存します。詳細については、データとファイルのストレージの概要をご覧ください。

MAC アドレスは使用しない

MAC アドレスはグローバルに一意であり、ユーザーはリセットできず、デバイスを初期化しても失われることはありません。そのため、通常、ユーザー ID として MAC アドレスを使用することは推奨されません。Android 10(API レベル 29)以降を搭載しているデバイスは、デバイス オーナー アプリではないすべてのアプリに対して、ランダム化された MAC アドレスをレポートします。

Android 6.0(API レベル 23)から Android 9(API レベル 28)までの場合、Wi-Fi や Bluetooth など、ローカル デバイスの MAC アドレスをサードパーティ API で使用することはできません。WifiInfo.getMacAddress() メソッドと BluetoothAdapter.getDefaultAdapter().getAddress() メソッドはいずれも 02:00:00:00:00:00 を返します。

また、Android 6.0 から Android 9 までの場合、Bluetooth や Wi-Fi のスキャンで検出された付近の外部デバイスの MAC アドレスにアクセスするには次のパーミッションが必要です。

メソッド / プロパティ 必要な権限
WifiManager.getScanResults() ACCESS_FINE_LOCATION または ACCESS_COARSE_LOCATION
BluetoothDevice.ACTION_FOUND ACCESS_FINE_LOCATION または ACCESS_COARSE_LOCATION
BluetoothLeScanner.startScan(ScanCallback) ACCESS_FINE_LOCATION または ACCESS_COARSE_LOCATION

識別子の特性

Android OS では、さまざまな動作特性を持つ多数の ID を使用できます。どの ID を使用するべきかは、以下の特性がそのユースケースでどのように機能するかによって判断します。ただし、これらの特性にはプライバシー上のリスクもあるため、特性同士の相互の関係を理解することが重要です。

スコープ

識別子のスコープとは、どのシステムがその識別子にアクセスできるかということです。通常、Android 識別子のスコープは次の 3 種類に分類されます。

  • 単一アプリ: 識別子はアプリの内部に存在し、他のアプリからはアクセスできません。
  • アプリグループ: 所定の関連アプリグループからアクセスできます。
  • デバイス: デバイスにインストールされたすべてのアプリからアクセスできます。

識別子に割り当てられたスコープが広いほど、識別子がトラッキングのために使用されるリスクが高くなります。反対に、単一のアプリ インスタンスからしかアクセスできない識別子であれば、複数のアプリでの複数のトランザクションを対象としたデバイスのトラッキングに使用することはできません。

リセット可能性と永続性

リセット可能性と永続性は、識別子の使用期間を定義し、識別子をリセットする方法を指定するものです。一般にリセットのトリガーとなるイベントは、アプリ内リセット、システム設定を介したリセット、起動時のリセット、インストール時のリセットなどです。Android の識別子の使用期間はさまざまですが、通常は ID のリセット方法と連動しています。

  • セッションのみ: ユーザーがアプリを再起動するたびに新しい ID が使用されます。
  • インストール リセット: ユーザーがアプリをアンインストールして再インストールするたびに新しい ID が使用されます。
  • FDR リセット: ユーザーがデバイスを初期化するたびに新しい ID が使用されます。
  • FDR 後存続: デバイスを初期化しても ID は失われません。

リセットできる場合、ユーザーは既存のプロファイル情報との関連付けが解除された新しい ID を作成できます。初期化後も存続する識別子など、識別子が存続する期間が長くなり安定性が高くなるほど、ユーザーが長期にわたるトラッキングの対象とされるリスクが高まります。アプリの再インストール時に識別子がリセットされる場合は、アプリ内やシステム設定からユーザーが明示的に識別子をリセットする手段がなくても、識別子の永続性が低くなり、ID をリセットする手段が提供されることになります。

一意性

一意性は、競合(対象のスコープ内に同一の識別子が存在すること)の可能性を決定します。一意性が最高レベルであれば、他のデバイスやアプリも含めて GUID の競合は起こりえません。それ以外の場合、一意性のレベルは、識別子の無秩序性と識別子作成時のソースのランダム性によって変わります。たとえば、暦上のインストール日(2019-03-01 など)で作成したランダム識別子の方が、インストール日の Unix タイムスタンプ(1551414181 など)で作成した識別子よりも、競合が発生する可能性が格段に高くなります。

一般に、ユーザー アカウントの識別子は一意であると見なすことができます。つまり、デバイスとアカウントの組み合わせごとに一意の ID があるということです。一方、母集団の中での一意性のレベルが低くなると、識別子を個々のユーザーのトラッキングに使用した場合の有用性が低くなるため、プライバシーの保護が強化されます。

整合性保護と否認防止

なりすましや再生成が難しい識別子を使用して、関連付けられているデバイスやアカウントに特定の特性があることを証明することができます。たとえば、スパム送信者が使用している仮想デバイスではないことを証明できます。なりすましが難しい識別子には、否認防止性もあります。デバイスから秘密鍵を使用してメッセージに署名すると、他の誰かのデバイスからメッセージが送信されたと主張することが困難になります。否認防止は、ユーザーにとって望ましいものであるとき(決済の認証に使用する場合など)もあれば、望ましくないものであるとき(送信すべきでないメッセージを送信してしまった場合など)もあります。

一般的なユースケースと使用すべき識別子

このセクションでは、IMEI などのハードウェア ID の代わりに使用できる識別子を紹介します。ハードウェア ID は、ユーザーがリセットできないこと、またデバイス全体がスコープとなることから推奨されません。多くの場合、アプリをスコープとする識別子で十分です。

ログアウトしたユーザーの設定をトラッキングする

ユーザー アカウントを含まない各デバイスの状態をサーバー側で保存する場合です。

使用する識別子: インスタンス ID または GUID

この識別子が推奨される理由

ユーザーが設定をリセットするためにアプリを再インストールする場合があるため、再インストール後も存続する情報を使用することは推奨されません。

ログアウトしたユーザーの設定を、署名キーが同じ複数のアプリでトラッキングする

各デバイスの状態をサーバー側で保存し、その状態を同じデバイス上にある同じキーで署名されている他のアプリに転送する場合です。

使用する識別子: SSAID

この識別子が推奨される理由

Android 8.0(API レベル 26)以降では、SSAID によって、同じデベロッパー署名キーで署名されているアプリの間で共通となる識別子が付与されます。そのため、ユーザーがアカウントにログインしなくてもアプリ間で状態を共有できます。

ログアウトしたユーザーの行動をトラッキングする

同じデバイス上の複数のアプリやセッションでの行動に基づき、ユーザーのプロフィールを作成した場合です。

使用する識別子: 広告 ID

この識別子が推奨される理由

Google Play デベロッパー コンテンツ ポリシーに基づき、広告のユースケースでは、ユーザーがリセットできる広告 ID を使用する必要があります。

ログアウトしたユーザーまたは匿名ユーザーの分析を行う

ログアウトしたユーザーまたは匿名ユーザーの使用統計情報と分析情報を計測する場合です。

使用する識別子: インスタンス ID または GUID(インスタンス ID では不十分な場合)

この識別子が推奨される理由

インスタンス ID や GUID は、各 ID を作成したアプリがスコープとなるため、複数のアプリでのユーザー トラッキングに使用されないようになっています。また、ユーザーがアプリのデータを消去したりアプリを再インストールしたりすることで簡単にリセットできます。インスタンス ID と GUID は簡単に作成できます。

  • インスタンス ID を作成する: Firebase Cloud Messaging ガイドをご覧ください。
  • GUID を作成する: アプリ内にロジックを実装します。次のコード スニペットをご覧ください。

    Kotlin

        val uniqueID: String = UUID.randomUUID().toString()
        

    Java

        String uniqueID = UUID.randomUUID().toString();
        

「収集するデータは匿名データである」とユーザーに告知しているデベロッパーは、識別子と PII(あるいは PII とリンクする可能性のある他の識別子)とをリンクさせることがないように注意してください

アプリごとの分析ができるモバイルアプリ向け Google アナリティクスを使用することもできます。

ログアウトしたユーザーのコンバージョンをトラッキングする

マーケティング戦略の成否を判断するためにコンバージョンをトラッキングする場合です。

使用する識別子: 広告 ID

この識別子が推奨される理由

広告関連のユースケースであり、複数のアプリで使用できる ID が必要となる可能性があるため、広告 ID が最適です。

複数のデバイスでの複数のインストールを処理する

同じユーザーの複数のデバイスにインストールされる各アプリ インスタンスを正しく特定する必要がある場合です。

使用する識別子: インスタンス ID または GUID

この識別子が推奨される理由

インスタンス ID は、まさにこの目的のために設計されています。スコープがそのアプリに限定されているため、複数のアプリ間でのユーザーのトラッキングには使用できません。また、再インストール時にリセットされます。まれに、インスタンス ID では不十分な場合は、GUID を使用することもできます。

不正行為防止: 無料コンテンツ制限を適用し、シビル攻撃を検知する

ユーザーがデバイスで表示できる無料記事などの無料コンテンツの数を制限する場合です。

使用する識別子: インスタンス ID または GUID。Android 8.0(API レベル 26)以降ではアプリ署名キーをスコープとする SSAID も使用可。

この識別子が推奨される理由

GUID やインスタンス ID を使用すると、ユーザーがコンテンツ上限の適用を回避するにはアプリを再インストールしなければならなくなるため、ほとんどのユーザーに思いとどまらせることができます。それでも保護が十分でないという場合は、Android の DRM API を使用できます。APK ごとの識別子である Widevine ID を使用してコンテンツへのアクセスを制限できます。

携帯通信会社の機能

アプリが携帯通信会社のアカウントを使用してデバイスの通話機能やテキスト メッセージ機能を操作する場合です。

使用する識別子: IMEI、IMSI、Line1

この識別子が推奨される理由

携帯通信会社に関連する機能に必要な場合にはハードウェア識別子の使用が認められます。たとえば、ハードウェア識別子を使用して、他の携帯通信会社や SIM スロットに切り替えたり、IP(Line1 の場合) - SIM ベースのユーザー アカウントで SMS メッセージを送信したりできます。ただし、特権のないアプリの場合は、アカウント ログインを使用してサーバー側でユーザー デバイス情報を取得することをおすすめします。Android 6.0(API レベル 23)以降ではランタイム権限を介してのみハードウェア識別子を使用できるようになっていることも理由の 1 つです。ユーザーがこの権限をオフにする可能性があるため、アプリの方で例外を適切に処理する必要があります。

不正使用の検知: ボットや DDoS 攻撃を特定する

デベロッパーのバックエンド サービスを攻撃している複数の偽のデバイスを検出しようとしている場合です。

使用する識別子: SafetyNet API

この識別子が推奨される理由

単独の識別子では、デバイスが正規のものであるかどうかの判定が難しくなります。エミュレータや、別のデバイスになりすましたコードではなく、正規の Android デバイスからリクエストが送信されているか検証するには、Safetynet API の attest() メソッドを使用して、リクエストを実行しているデバイスの信頼性を検証します。詳細については、SafetyNet API のドキュメントをご覧ください。

不正行為や不正使用の検知: 高価値認証情報の盗難を検知する

1 つのデバイスが盗まれた高価値認証情報で(不正な決済などに)複数回使用されていないかどうかを検知しようとしている場合です。

使用する識別子: 不正行為防止には、その性質上、独自のシグナルが必要となり、またそうしたシグナルは時とともに変わる可能性があるため、本書では取り扱いません。ただし、IMEI や IMSI などのハードウェア ID は、ルート権限を取得されたデバイスやエミュレートされたデバイスでは簡単に変更できるため、不正行為の指標としては信頼できません。