サーバー側の検証コールバックは、Google により展開されたクエリ パラメータを含む URL リクエストです。このコールバックが外部システムに送信され、ユーザーにリワード広告またはリワード インタースティシャル広告の視聴に対する報酬が必要であることを通知します。リワード SSV(サーバーサイド認証)コールバックは、ユーザーに報酬を与えるクライアント側のコールバックのなりすましに対する追加の保護層となります。
このガイドでは、サードパーティの暗号ライブラリ Tink Java Apps を使用して、リワード SSV コールバックを検証し、コールバックのクエリ パラメータが正当な値であることを確認する方法を説明します。このガイドでは Tink を使用していますが、ECDSA をサポートするサードパーティのライブラリであればどれでも使用できます。また、AdMob 管理画面のテストツールでサーバーをテストすることもできます。
機能が保証されたこちらの Java Spring Boot の使用例をご確認ください。
前提条件
Google Mobile Ads SDKv3.12.0 以降の Google Mobile Ads Unity プラグインが必要となります。
広告ユニットでリワードのサーバー側の検証を有効にしていること
Tink Java Apps ライブラリの RewardedAdsVerifier を使用する
Tink Java Apps GitHub リポジトリには RewardedAdsVerifier
ヘルパークラスが含まれているため、リワード SSV コールバックの検証に必要なコードは少なくて済みます。このクラスを使用すると、次のコードでコールバック URL を検証できます。
RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
.fetchVerifyingPublicKeysWith(
RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
.build();
String rewardUrl = ...;
verifier.verify(rewardUrl);
verify()
メソッドが例外を発生せずに実行された場合、コールバック URL は正常に検証されています。ユーザーに報酬を与えるセクションには、報酬を付与するタイミングについてのおすすめの方法が記載されています。リワード SSV コールバックを検証するためにこのクラスで実行される手順については、リワード SSV の手動検証セクションを参照してください。
SSV コールバック パラメータ
サーバー側の検証コールバックには、リワード広告のインタラクションを説明するクエリ パラメータが含まれています。パラメータ名、説明、値の例を以下に示します。パラメータはアルファベット順に送信されます。
パラメータ名 | 説明 | 値の例 |
---|---|---|
ad_network | この広告を配信した広告ソースの広告ソース ID。ID 値に対応する広告ソース名は、広告ソース ID セクションの一覧で確認できます。 | 1953547073528090325 |
ad_unit | リワード広告をリクエストするために使用された AdMob 広告ユニット ID。 | 2747237135 |
custom_data | で指定されたカスタムデータ文字列。
アプリでカスタムデータ文字列が指定されていない場合、このクエリ パラメータ値は SSV コールバックに存在しません。 |
SAMPLE_CUSTOM_DATA_STRING |
key_id | SSV コールバックを検証するために使用される鍵。この値は、AdMob 鍵サーバーによって提供される公開鍵にマッピングされます。 | 1234567890 |
reward_amount | 広告ユニットの設定で指定された報酬額。 | 5 |
reward_item | 広告ユニットの設定で指定された報酬アイテム。 | コイン |
署名 | AdMob によって生成された SSV コールバックの署名。 | MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY |
タイムスタンプ | ユーザーが報酬を受けたときのエポック タイムスタンプ(ミリ秒単位)。 | 1507770365237823 |
transaction_id | AdMob によって生成された報酬付与イベントごとに固有の 16 進数でエンコードされた ID。 | 18fa792de1bca816048293fc71035638 |
user_id |
SetUserId .
アプリでユーザー ID が指定されていない場合、このクエリ パラメータは SSV コールバックに存在しません。 |
1234567 |
広告ソース ID
広告ソースの名前と ID
広告ソース名 | 広告ソース ID |
---|---|
Aarki(入札) | 5240798063227064260 |
広告の生成(入札) | 1477265452970951479 |
AdColony | 15586990674969969776 |
AdColony(非 SDK)(入札) | 4600416542059544716 |
AdColony(入札) | 6895345910719072481 |
AdFalcon | 3528208921554210682 |
AdMob ネットワーク | 5450213213286189855 |
ADResult | 10593873382626181482 |
AMoAd | 17253994435944008978 |
Applovin | 1063618907739174004 |
Applovin(入札) | 1328079684332308356 |
チャートブースト | 2873236629771172317 |
Chocolate Platform(入札) | 6432849193975106527 |
CrossChannel(MdotM) | 9372067028804390441 |
Custom Event | 18351550913290782395 |
DT Exchange* * 2022 年 9 月 21 日より前までは、このネットワークは「Fyber Marketplace」と呼ばれていました。 | 2179455223494392917 |
EMX(入札) | 8497809869790333482 |
Fluct(入札) | 8419777862490735710 |
Flurry | 3376427960656545613 |
Fyber* * この広告ソースは過去のレポートに使用されます。 | 4839637394546996422 |
i-mobile | 5208827440166355534 |
デジタルの改善(入札) | 159382223051638006 |
インデックス エクスチェンジ(入札) | 4100650709078789802 |
InMobi | 7681903010231960328 |
InMobi(入札) | 6325663098072678541 |
IronSource | 6925240245545091930 |
ironSource の広告(入札) | 1643326773739866623 |
Leadbolt | 2899150749497968595 |
LG U+AD | 18298738678491729107 |
LINE 広告ネットワーク | 3025503711505004547 |
maio | 7505118203095108657 |
maio(入札) | 1343336733822567166 |
Media.net(入札) | 2127936450554446159 |
メディエーション向け自社広告 | 6060308706800320801 |
Meta Audience Network* * 2022 年 6 月 6 日以前は「Facebook Audience Network」と呼ばれていました。 | 10568273599589928883 |
Meta Audience Network(入札)* * 2022 年 6 月 6 日より前は、このネットワークは「Facebook Audience Network(入札)」と呼ばれていました。 | 11198165126854996598 |
メインテグラル | 1357746574408896200 |
Mintegral(入札) | 6250601289653372374 |
MobFox | 8079529624516381459 |
MobFox(入札) | 3086513548163922365 |
MoPub(非推奨) | 10872986198578383917 |
myTarget | 8450873672465271579 |
Nend | 9383070032774777750 |
Nexxen(入札)* * 2024 年 5 月 1 日より前までは、このネットワークは「UnrulyX」と呼ばれていました。 | 2831998725945605450 |
ONE by AOL(Millennial Media) | 6101072188699264581 |
ONE by AOL(Nexage) | 3224789793037044399 |
OneTag Exchange(入札) | 4873891452523427499 |
OpenX(入札) | 4918705482605678398 |
Pangle(入札) | 3525379893916449117 |
PubMatic(入札) | 3841544486172445473 |
純広告キャンペーン | 7068401028668408324 |
RhythmOne(入札) | 2831998725945605450 |
Rubicon(入札) | 3993193775968767067 |
SK 惑星 | 734341340207269415 |
シェアスルー(入札) | 5247944089976324188 |
Smaato(入札) | 3362360112145450544 |
Equativ(入札)* * 2023 年 1 月 12 日より前までは、このネットワークは「Smart Adserver」と呼ばれていました。 | 5970199210771591442 |
Sonobi(入札) | 3270984106996027150 |
Tapjoy | 7295217276740746030 |
Tapjoy(入札) | 4692500501762622178 |
テンセント GDT | 7007906637038700218 |
TripleLift(入札) | 8332676245392738510 |
Unity 広告 | 4970775877303683148 |
Unity 広告(入札) | 7069338991535737586 |
グーグルメディア | 7360851262951344112 |
Verve グループ(入札) | 5013176581647059185 |
VPN | 1940957084538325905 |
Liftoff Monetize* * 2023 年 1 月 30 日までは、このネットワークは「Vungle」と呼ばれていました。 | 1953547073528090325 |
Liftoff Monetize(入札)* * 2023 年 1 月 30 日までは、このネットワークは「Vungle(入札)」と呼ばれていました。 | 4692500501762622185 |
収益 mo(入札) | 4193081836471107579 |
収益 One(入札) | 3154533971590234104 |
Zucks | 5506531810221735863 |
ユーザーに特典を提供する
ユーザーに報酬を付与するタイミングを決める際には、ユーザー エクスペリエンスと報酬検証のバランスをとることが重要です。サーバー側のコールバックでは、外部システムに達する前に遅延が発生する可能性があります。したがって、クライアント側のコールバックを使用して直ちにユーザーに報酬を提供し、サーバー側のコールバックの受信時にすべての報酬の検証を実行することをおすすめします。このアプローチにより、優れたユーザー エクスペリエンスが提供されると同時に、付与された報酬の有効性が保証されます。
ただし、報酬の有効性が重要で(報酬がアプリのゲーム内経済に影響を与えるなど)、報酬付与の遅延が許容されるアプリの場合は、サーバー側のコールバックが確認されるまで待つことをおすすめします。
カスタムデータ
サーバーサイド認証コールバックで追加データを必要とするアプリでは、リワード広告のカスタムデータ機能を使用する必要があります。リワード広告オブジェクトに設定されている文字列値はすべて、SSV コールバックの custom_data
クエリ パラメータに受け渡されます。カスタムデータ値が設定されていない場合、SSV コールバックは custom_data
クエリ パラメータ値を持ちません。
次のコード例は、リワード広告の読み込み後に SSV の各種オプションを設定する方法を示したものです。
void HandleRewardedAdLoaded(RewardedAd ad, AdFailedToLoadEventArgs error) { // Create and pass the SSV options to the rewarded ad. var options = new ServerSideVerificationOptions .Builder() .SetCustomData("SAMPLE_CUSTOM_DATA_STRING") .Build() ad.SetServerSideVerificationOptions(options); }
カスタム リワード文字列を設定する場合は、広告を表示する前に行う必要があります。
リワード SSV の手動検証
RewardedAdsVerifier
クラスがリワード SSV を検証するために実行する手順を下記で説明します。含まれているコード スニペットは Java であり、Tink サードパーティ ライブラリを使用していますが、ECDSA をサポートするサードパーティのライブラリを使用して、これらの手順を任意の言語で実装することもできます。
公開鍵を取得する
リワード SSV コールバックを検証するには、AdMob から提供された公開鍵が必要です。
リワード SSV コールバックを検証するために使用される公開鍵のリストは、AdMob 鍵サーバーから取得できます。公開鍵のリストは、次のような形式の JSON 表現として提供されます。
{
"keys": [
{
keyId: 1916455855,
pem: "-----BEGIN PUBLIC KEY-----\nMF...YTPcw==\n-----END PUBLIC KEY-----"
base64: "MFkwEwYHKoZIzj0CAQYI...ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw=="
},
{
keyId: 3901585526,
pem: "-----BEGIN PUBLIC KEY-----\nMF...aDUsw==\n-----END PUBLIC KEY-----"
base64: "MFYwEAYHKoZIzj0CAQYF...4akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw=="
},
],
}
公開鍵を取得するには、AdMob 鍵サーバーに接続して鍵をダウンロードします。次のコードはこのタスクを実行し、鍵の JSON 表現を data
変数に保存します。
String url = ...;
NetHttpTransport httpTransport = new NetHttpTransport.Builder().build();
HttpRequest httpRequest =
httpTransport.createRequestFactory().buildGetRequest(new GenericUrl(url));
HttpResponse httpResponse = httpRequest.execute();
if (httpResponse.getStatusCode() != HttpStatusCodes.STATUS_CODE_OK) {
throw new IOException("Unexpected status code = " + httpResponse.getStatusCode());
}
String data;
InputStream contentStream = httpResponse.getContent();
try {
InputStreamReader reader = new InputStreamReader(contentStream, UTF_8);
data = readerToString(reader);
} finally {
contentStream.close();
}
公開鍵は定期的にローテーションされることに注意してください。次回のローテーションについては、メールでお知らせが届きます。公開鍵をキャッシュしている場合は、このメールを受け取った時点で鍵を更新してください。
取得された公開鍵は、解析される必要があります。以下の parsePublicKeysJson
メソッドは、上記の例のような JSON 文字列を入力として受け取り、key_id
値から公開鍵へのマッピングを作成します。公開鍵は Tink ライブラリの ECPublicKey
オブジェクトとしてカプセル化されます。
private static Map<Integer, ECPublicKey> parsePublicKeysJson(String publicKeysJson)
throws GeneralSecurityException {
Map<Integer, ECPublicKey> publicKeys = new HashMap<>();
try {
JSONArray keys = new JSONObject(publicKeysJson).getJSONArray("keys");
for (int i = 0; i < keys.length(); i++) {
JSONObject key = keys.getJSONObject(i);
publicKeys.put(
key.getInt("keyId"),
EllipticCurves.getEcPublicKey(Base64.decode(key.getString("base64"))));
}
} catch (JSONException e) {
throw new GeneralSecurityException("failed to extract trusted signing public keys", e);
}
if (publicKeys.isEmpty()) {
throw new GeneralSecurityException("No trusted keys are available.");
}
return publicKeys;
}
検証するコンテンツを取得する
リワード SSV コールバックの最後の 2 つのクエリ パラメータは、signature
と key_id,
で、常にこの順番となります。残りのクエリ パラメータは、検証されるコンテンツを指定します。リワード コールバックを https://www.myserver.com/mypath
に送信するように AdMob を設定するとしましょう。以下のスニペットは、検証されるコンテンツがハイライト表示されたリワード SSV コールバックの例を示しています。
https://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins ×tamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887
以下のコードは、検証するコンテンツを UTF-8 バイト配列としてコールバック URL から解析する方法を示しています。
public static final String SIGNATURE_PARAM_NAME = "signature=";
...
URI uri;
try {
uri = new URI(rewardUrl);
} catch (URISyntaxException ex) {
throw new GeneralSecurityException(ex);
}
String queryString = uri.getQuery();
int i = queryString.indexOf(SIGNATURE_PARAM_NAME);
if (i == -1) {
throw new GeneralSecurityException("needs a signature query parameter");
}
byte[] queryParamContentData =
queryString
.substring(0, i - 1)
// i - 1 instead of i because of & in the query string
.getBytes(Charset.forName("UTF-8"));
コールバック URL から署名と key_id を取得する
前の手順の queryString
値を使用して、以下に示すように、コールバック URL の signature
と key_id
のクエリ パラメータを解析します。
public static final String KEY_ID_PARAM_NAME = "key_id=";
...
String sigAndKeyId = queryString.substring(i);
i = sigAndKeyId.indexOf(KEY_ID_PARAM_NAME);
if (i == -1) {
throw new GeneralSecurityException("needs a key_id query parameter");
}
String sig =
sigAndKeyId.substring(
SIGNATURE_PARAM_NAME.length(), i - 1 /* i - 1 instead of i because of & */);
int keyId = Integer.valueOf(sigAndKeyId.substring(i + KEY_ID_PARAM_NAME.length()));
検証を行う
最後のステップは、適切な公開鍵でコールバック URL の内容を検証することです。parsePublicKeysJson
メソッドから返されたマッピングを取得し、コールバック URL の key_id
パラメータを使用して、そのマッピングから公開鍵を取得します。次に、その公開鍵で署名を検証します。これらのステップは、以下の verify
メソッドで示されています。
private void verify(final byte[] dataToVerify, int keyId, final byte[] signature)
throws GeneralSecurityException {
Map<Integer, ECPublicKey> publicKeys = parsePublicKeysJson();
if (publicKeys.containsKey(keyId)) {
foundKeyId = true;
ECPublicKey publicKey = publicKeys.get(keyId);
EcdsaVerifyJce verifier = new EcdsaVerifyJce(publicKey, HashType.SHA256, EcdsaEncoding.DER);
verifier.verify(signature, dataToVerify);
} else {
throw new GeneralSecurityException("cannot find verifying key with key ID: " + keyId);
}
}
メソッドが例外をスローせずに実行された場合、コールバック URL は正常に検証されています。
よくある質問
- AdMob 鍵サーバーから提供された公開鍵をキャッシュすることはできますか?
- SSV コールバックの検証に必要なオペレーションの数を減らすために、AdMob 鍵サーバーから提供された公開鍵をキャッシュすることをおすすめします。ただし、公開鍵は定期的にローテーションされるため、24 時間以上キャッシュしないでください。
- AdMob 鍵サーバーによって提供される公開鍵はどのくらいの頻度でローテーションされますか?
- AdMob 鍵サーバーによって提供される公開鍵は、可変スケジュールでローテーションされます。SSV コールバックの検証が引き続き意図したとおりに機能するように、公開鍵は 24 時間以上キャッシュしないでください。
- サーバーにアクセスできない場合はどうなりますか?
- SSV コールバック用に
HTTP 200 OK
成功ステータス レスポンス コードが必要です。サーバーにアクセスできない場合、または想定どおりのレスポンスが得られない場合、Google は 1 秒間隔で最大 5 回まで SSV コールバックの送信を再試行します。 - SSV コールバックが Google から送信されていることを確認するにはどうすればよいですか?
- SSV コールバックが Google から送信されていることを確認するには、リバース DNS ルックアップを使用します。