6 月 3 日の「#Android11: The Beta Launch Show」にぜひご参加ください。

アプリへのサーバーサイド ライセンス確認の追加

ユーザーが Google Play ストアからアプリの正規コピーを購入またはダウンロードしたことを確認する場合は、管理しているサーバーでライセンス確認のチェックを行うことをおすすめします。

このガイドでは、サーバーサイド ライセンス確認プロセスの各段階と、そのチェックの実施に関するおすすめの方法について説明します。

プロセスの概要

図 1 は、アプリ、Google Play、プライベート サーバーの間での情報の流れを示しています。

データフロー図
図 1. アプリと Google Play 間、アプリとプライベート サーバー間のデータの流れ
  1. アプリから Google Play へのリクエストで、特定のユーザーがアプリの正規コピーを購入またはダウンロードしているかどうかの問い合わせが行われます。
  2. Google Play からアプリへの応答として、「応答データ オブジェクト」ResponseData 型のオブジェクト)が送られます。このオブジェクトは、ユーザーがアプリの正規コピーを購入またはダウンロードしたかどうかを示す署名付きの情報です。
  3. アプリから、パブリッシャー側で管理するプライベート サーバーに対して、応答データの内容を確認するようリクエストがなされます。
  4. プライベート サーバーからアプリへの応答として、ユーザーが実際にアプリの正規コピーを購入またはダウンロードしたかどうかを示すステータスが送られます。サーバーから「成功」メッセージが送られた場合は、応答の検証を行い、その後でライセンスを必要とするリソースの使用をユーザーに許可します。

応答データは Google Play によって署名され、プライベート サーバーでチェックされるため、アプリを実行しているデバイス上で応答オブジェクトが変更されることはありません。サーバーを利用して正規ユーザーのみにリソースの使用を許可することで、不正なユーザーによる使用を有効に防止できます。

次のセクションでは、サーバーサイド ライセンス確認を行う場合に追加で考慮すべき事項を示します。

リプレイ攻撃への対策

自分のライセンス ステータスに関する Google Play からの応答を受け取ったユーザーは、その応答データをコピーして複数回使用したり、他のユーザーに与えたりできます。そのユーザーは、応答データを自分のリクエストに偽造して、アプリのプライベート サーバーに送信する可能性があります。このような行為は「リプレイ攻撃」と呼ばれます。

リプレイ攻撃が成功する可能性を減らすには、プライベート サーバーにリクエストを送る前に以下の対策を行います。

  • 応答データに含まれるタイムスタンプを確認し、応答が Google Play によって最近生成されたことを確認します。

  • アプリからプライベート サーバーに同じ応答データが送られる回数を減らすため、指数バックオフなど、サーバー リクエストに対するレート制限をかけます。

  • プライベート サーバーで Google Play からの応答データの内容を確認する前に、プライベート サーバーに対して最初の認証ベースのリクエストを行います。この最初のリクエストでは、ユーザーの認証情報をサーバーに送信し、サーバーからは「nonce」(使い捨ての数値)を付けて応答させるようにします。そして、プライベート サーバーへの次のリクエストにはこの nonce を含め、ライセンス確認データを要求します。適切な nonce 値を選択する方法の詳細については、適切な nonce 値の生成セクションをご覧ください。

適切な nonce 値の生成

次のいずれかの手法を使用して、推測が困難な nonce 値を作成します。

  • ユーザーの ID に基づいてハッシュ値を生成する。
  • ユーザーごとにランダムな値を生成する。このランダム値を、そのユーザーの属性の一部としてプライベート サーバーに保存する。

プライベート サーバーからの応答データの検証

プライベート サーバーからアプリに送信された応答データをレビューするときは、License Verification Library(LVL)の応答が偽造されていないことを確認します。プライベート サーバーからの応答データに含まれる署名を、前の手順でアプリが Google Play から受け取った鍵と比較することで検証します。

また、署名されている部分は、LVL 固有のブロックのみということも重要です。つまり、プライベート サーバーからの応答データの中で、アプリで信用すべきなのはその部分のみということです。