レシート検証によって、ユーザーが購入していないコンテンツにアクセスすることを防ぎます。
レシートはアプリケーションのコンテンツを配布する時点で検証するのが最も効率的です。
ユーザーが購入するコンテンツがすでにデバイスに存在する場合は、アプリケーションはただ、それをアンロックするかどうかを決定するだけです。
Unity IAP はコンテンツを隠し、Google Play と App Store をとおしてレシートを検証し分析するツールを提供します。
レシート検証は、アプリケーションの Google Play 公開キーや Apple のルート証明書などの既知の暗号化キーを使用して実行されます。
攻撃者がこれらの機密情報を置き換えることができると、レシート検証は失敗してしまいます。そのため、攻撃者が簡単にこれらのキーを見つけ、改変できないようにすることが大切です。
Unity IAP はアプリケーション内の秘密鍵を見つかりにくくするツールを提供します。これにより鍵をわかりにくくするため、攻撃者のアクセスをより難しくします。Unity のメニューで Window > Unity IAP > IAP Receipt Validation Obfuscator を選んでアクセスできます。
このウィンドウでは、Unity IAP に束ねられた Apple の ルート証明書と、アプリケーションのGoogle Play Developer Console の Services & APIs ページから取得する Google Play 公開キーを両方とも、2つの異なる C# ファイル、 AppleTangle と GooglePlayTangle にエンコードします。それらのC# ファイルは、プロジェクトに加えられ、次のセクションで使用されます。
注意: Apple ストアだけをターゲットにする場合は、Google Play 公開キーは必要ではありません。同様にGoogle Play だけをターゲットにする場合は、Apple のルート証明書は必要ではありません。
CrossPlatformValidator
クラスは、Google Play と Apple ストア両方の検証に使用することができます。
このクラスを使用するには、Google Play 公開キーか Apple の root 証明書が必要です。また、両方のプラットフォームを検証するなら両方が必要です。
CrossPlatformValidator
は 2つの検証を行います。
バリデータは Google Play と Apple プラットフォームで作成されたレシートのみを検証することに注意してください。エディターで作成された偽造を含め、その他のプラットフォームで作成されたものには、IAPSecurityException 例外が発生します。
必要な機密を提供していないプラットフォームのレシートを検証しようとすると、MissingStoreSecretException 例外が発生します。
public PurchaseProcessingResult ProcessPurchase (PurchaseEventArgs e)
{
bool validPurchase = true; // R.V. のないプラットフォームに有効です
// Unity IAP の検証ロジックはこれらのプラットフォームにのみ含まれます。
# if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
// エディターの難読化ウィンドウで準備した機密を持つ
// バリデーターを準備します。
var validator = new CrossPlatformValidator(GooglePlayTangle.Data(),
AppleTangle.Data(), Application.bundleIdentifier);
try {
// Google Play で、result は 1 つの product ID を取得します
// Apple stores で、receipts には複数のプロダクトが含まれます
var result = validator.Validate(e.purchasedProduct.receipt);
// 情報提供の目的で、ここにレシートをリストします
Debug.Log("Receipt is valid. Contents:");
foreach (IPurchaseReceipt productReceipt in result) {
Debug.Log(productReceipt.productID);
Debug.Log(productReceipt.purchaseDate);
Debug.Log(productReceipt.transactionID);
}
} catch (IAPSecurityException) {
Debug.Log("Invalid receipt, not unlocking content");
validPurchase = false;
}
# endif
if (validPurchase) {
// 適当なコンテンツをここにアンロックします
}
return PurchaseProcessingResult.Complete;
}
レシートの有効性だけをチェックするのではなく、それに含まれる情報をチェックすることも大切です。ハッカーが実際に購入せずにコンテンツにアクセスする一般的な攻撃方法は、他のプロダクトやアプリケーションのレシートを提供することです。それらのレシートは純正なので、検証に引っかかりません。そのため、CrossPlatformValidator で分析したプロダクト ID に基づいて判断する必要があります。
それぞれのストアの購入レシートには、異なる欄があります。そのストア特有の欄にアクセスするには、 IPurchaseReceipt
を 2つの異なるサブタイプ GooglePlayReceipt
と AppleInAppPurchaseReceipt
にダウンキャストすることができます。
var result = validator.Validate(e.purchasedProduct.receipt);
Debug.Log("Receipt is valid. Contents:");
foreach (IPurchaseReceipt productReceipt in result) {
Debug.Log(productReceipt.productID);
Debug.Log(productReceipt.purchaseDate);
Debug.Log(productReceipt.transactionID);
GooglePlayReceipt google = productReceipt as GooglePlayReceipt;
if (null != google) {
// ここに Google のオーダー ID
// sandbox でテストする場合は null にするように注意
// なぜなら、Google の sandbox はオーダー IDを発行しないため
Debug.Log(google.transactionID);
Debug.Log(google.purchaseState);
Debug.Log(google.purchaseToken);
}
AppleInAppPurchaseReceipt apple = productReceipt as AppleInAppPurchaseReceipt;
if (null != apple) {
Debug.Log(apple.originalTransactionIdentifier);
Debug.Log(apple.subscriptionExpirationDate);
Debug.Log(apple.cancellationDate);
Debug.Log(apple.quantity);
}
}
AppleValidator
クラスは、Apple レシートの詳細な情報を取り出すのに使用されます。このクラスは、Apple の Deprecated トランザクション レシートではなく、iOS 7 以降のバージョンの App Store レシートにのみ正しく機能することに気を付けてください。
# if UNITY_ANDROID || UNITY_IOS || UNITY_STANDALONE_OSX
var builder = ConfigurationBuilder.Instance(StandardPurchasingModule.Instance());
// IAP 初期化の間に、IAppleConfiguration への参照を取得
var appleConfig = builder.Configure<IAppleConfiguration>();
var receiptData = System.Convert.FromBase64String(appleConfig.appReceipt);
AppleReceipt receipt = new AppleValidator(AppleTangle.Data()).Validate(receiptData);
Debug.Log(receipt.bundleID);
Debug.Log(receipt.receiptCreationDate);
foreach (AppleInAppPurchaseReceipt productReceipt in receipt.inAppPurchaseReceipts) {
Debug.Log(productReceipt.transactionIdentifier);
Debug.Log(productReceipt.productIdentifier);
}
# endif
AppleReceipt
タイプは Apple の ASN1 receipt 形式をモデルにしています。詳細は Apple のドキュメンテーション を参照してください。