驗證伺服器端驗證 (SSV) 回呼

伺服器端驗證回呼是包含查詢參數的網址要求 方法是 Google 將資料擴展至外部系統 提醒使用者該與獎勵廣告互動或 插頁式獎勵廣告。獎勵 SSV (伺服器端驗證) 回呼 額外增添一層防護,防止用戶端回呼遭到假冒 來獎勵使用者

本指南將說明如何使用 Tink Java 應用程式第三方 來確保回呼中的查詢參數 正當價值 雖然本指南會將 Tink 用於本指南,但您還是可以選擇 採用 ECDSA: 您也可以使用測試工具 工具。

請造訪這個網頁 示例 使用 Java Spring-boot。

必要條件

  • 獎勵廣告整合至 的行動應用程式 Google 行動廣告 Unity 外掛程式 3.12.0 版或更新版本。

  • 啟用獎勵伺服器端 驗證

使用 Tink Java 應用程式程式庫中的 獎勵 AdsVerifier

Tink Java Apps GitHub 存放區 包含 RewardedAdsVerifier敬上 輔助類別,減少驗證獎勵 SSV 回呼所需的程式碼。 使用此類別時,您可以使用下列程式碼驗證回呼網址。

RewardedAdsVerifier verifier = new RewardedAdsVerifier.Builder()
    .fetchVerifyingPublicKeysWith(
        RewardedAdsVerifier.KEYS_DOWNLOADER_INSTANCE_PROD)
    .build();
String rewardUrl = ...;
verifier.verify(rewardUrl);

如果 verify() 方法在未引發例外狀況的情況下執行,回呼 已成功驗證網址。獎勵使用者 。換 此類別為了驗證獎勵 SSV 回呼而執行的步驟細目。 您可以詳閱手動驗證獎勵廣告 SSV 部分。

SSV 回呼參數

伺服器端驗證回呼包含查詢參數,用來說明 獎勵廣告互動。參數名稱、說明和範例值如下: 。參數會依字母順序傳送。

參數名稱 說明 範例值
ad_network 提供這則廣告的廣告來源廣告來源 ID。廣告來源 與編號值對應的名稱會列在《 來源 ID 區段, 1953547073528090325
ad_unit 用來請求獎勵廣告的 AdMob 廣告單元 ID。 2747237135
key_id 用於驗證 SSV 回呼的金鑰。這個值對應至公開金鑰 由 AdMob 金鑰伺服器提供的圖示組成 1234567890
reward_amount 廣告單元設定中指定的獎勵金額。 5
reward_item 廣告單元設定中指定的獎勵項目。 硬幣
簽章 AdMob 產生的 SSV 回呼簽名。 MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
timestamp 使用者獲得獎勵的時間戳記,以毫秒為單位。 1507770365237823
transaction_id 由 AdMob 產生的每個獎勵公益活動的專屬十六進位編碼 ID。 18fa792de1bca816048293fc71035638
user_id 使用者提供的使用者 ID SetUserId

如果應用程式未提供使用者 ID,這個查詢參數就不會 都會出現在 SSV 回呼中。

1234567

廣告來源 ID

廣告來源名稱和 ID

广告来源名称 广告来源 ID
Aarki(出价)5240798063227064260
Ad Generation(出价)1477265452970951479
AdColony 15586990674969969776
AdColony(非 SDK)(出价)4600416542059544716
AdColony(出价)6895345910719072481
AdFalcon3528208921554210682
AdMob 广告联盟5450213213286189855
AdMob 广告联盟广告瀑布流1215381445328257950
ADResult10593873382626181482
AMoAd17253994435944008978
AppLovin1063618907739174004
AppLovin(出价)1328079684332308356
Chartboost2873236629771172317
Chocolate Platform(出价)6432849193975106527
跨渠道 (MdotM)9372067028804390441
自定义事件18351550913290782395
DT Exchange*
* 在 2022 年 9 月 21 日之前,该广告联盟称为“Fyber Marketplace”。
2179455223494392917
EMX(出价)8497809869790333482
Fluct(出价)8419777862490735710
小风3376427960656545613
Fyber*
* 此广告来源用于生成历史报告。
4839637394546996422
i-mobile5208827440166355534
优化数字化(出价)159382223051638006
Index Exchange(出价)4100650709078789802
InMobi7681903010231960328
InMobi(出价)6325663098072678541
InMobi Exchange(出价)5264320421916134407
IronSource6925240245545091930
ironSource Ads(出价)1643326773739866623
Leadbolt2899150749497968595
LG U+AD18298738678491729107
LINE 广告联盟3025503711505004547
maio7505118203095108657
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
Mintegral1357746574408896200
Mintegral(出价)6250601289653372374
MobFox8079529624516381459
MobFox(出价)3086513548163922365
MoPub(已弃用10872986198578383917
myTarget8450873672465271579
Nend9383070032774777750
Nexxen(出价)*

* 在 2024 年 5 月 1 日之前,该广告联盟称为“UnrulyX”。

2831998725945605450
ONE by AOL (Millennial Media)6101072188699264581
ONE by AOL (Nexage)3224789793037044399
OneTag Exchange(出价)4873891452523427499
OpenX(出价)4918705482605678398
邦格尔4069896914521993236
Pangle(出价)3525379893916449117
PubMatic(出价)3841544486172445473
预订型广告系列7068401028668408324
RhythmOne(出价)2831998725945605450
Rubicon(出价)3993193775968767067
SK 星球734341340207269415
Sharethrough(出价)5247944089976324188
Smaato(出价)3362360112145450544
Equativ(出价)*

* 在 2023 年 1 月 12 日之前,该广告联盟称为“Smart Adserver”。

5970199210771591442
Sonobi(出价)3270984106996027150
Tapjoy7295217276740746030
Tapjoy(出价)4692500501762622178
Tencent GDT7007906637038700218
TripleLift(出价)8332676245392738510
Unity 广告4970775877303683148
Unity Ads(出价)7069338991535737586
Verizon Media7360851262951344112
Verve Group(出价)5013176581647059185
Vpon1940957084538325905
Liftoff Monetize*

* 在 2023 年 1 月 30 日之前,该广告联盟称为“Vungle”。

1953547073528090325
Liftoff Monetize(出价)*

* 在 2023 年 1 月 30 日之前,该广告联盟称为“Vungle(出价)”。

4692500501762622185
Yieldmo(出价)4193081836471107579
YieldOne(出价)3154533971590234104
Zucks5506531810221735863

獎勵使用者

決定時,應在使用者體驗和獎勵驗證之間取得平衡 何時應獎勵使用者伺服器端回呼可能會在下列時間之前發生延遲: 才能觸及外部系統因此,建議的最佳做法就是 用戶端回呼以立即獎勵使用者,同時執行 並在收到伺服器端回呼時驗證所有獎勵。這個 的做法能提供良好的使用者體驗,同時確保授予的 獎勵。

但對於獎勵有效性至關重要的應用程式 (例如 獎勵會影響應用程式的遊戲內經濟生態) 及提供獎勵的時間延遲 接受,等待已驗證的伺服器端回呼可能是最佳選擇 。

自訂資料

如果應用程式需要在伺服器端驗證回呼中使用額外資料,則應使用 獎勵廣告的自訂資料功能獎勵廣告中設定的任何字串值 系統會將物件傳遞至 SSV 回呼的 custom_data 查詢參數。如果答案為「否」 如果已設定自訂資料值,custom_data 查詢參數將不會 傳遞到 SSV 回呼中。

以下程式碼範例示範如何在 已載入獎勵廣告。

private void LoadRewardedAd(string adUnitId)
{
  // Send the request to load the ad.
  AdRequest adRequest = new AdRequest();
  RewardedAd.Load(adUnitId, adRequest, (RewardedAd rewardedAd, LoadAdError error) =>
  {
    // If the operation failed with a reason.
    if (error != null)
    {
        Debug.LogError("Rewarded ad failed to load an ad with error : " + error);
        return;
    }

    var options = new ServerSideVerificationOptions
                          .Builder()
                          .SetCustomData("SAMPLE_CUSTOM_DATA_STRING")
                          .Build()
    rewardedAd.SetServerSideVerificationOptions(options);
  });
}
敬上

如要設定自訂獎勵字串,請先完成相關設定再顯示 廣告。

手動驗證獎勵兩步驟驗證

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 回呼的最後兩個查詢參數一律為 signaturekey_id,。其餘查詢參數會指定 以進行驗證假設您已設定 AdMob 傳送獎勵回呼給 https://www.myserver.com/mypath。以下程式碼片段是獎勵廣告範例 SSV 回呼,其中包含要驗證的內容。

https://www.myserver.com/path?ad_network=54...55&ad_unit=12345678&reward_amount=10&reward_item=coins
&timestamp=150777823&transaction_id=12...DEF&user_id=1234567&signature=ME...Z1c&key_id=1268887

下列程式碼示範如何剖析要從 做為 UTF-8 位元組陣列的回呼網址。

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"));

從回呼網址取得署名和 key_id

使用上一步驟的 queryString 值剖析 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()));

執行驗證

最後一步是使用 適用的公開金鑰取得從 parsePublicKeysJson 方法,並使用回呼中的 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);
  }
}

如果方法在未擲回例外狀況的情況下執行,則回呼網址之前為 已成功驗證。

常見問題

我可以快取 AdMob 金鑰伺服器提供的公開金鑰嗎?
建議您快取 AdMob 金鑰提供的公開金鑰 ,減少驗證 SSV 所需的作業次數 回呼函式。不過請注意,公開金鑰會定期輪替,因此不應 超過 24 小時的快取時間。
AdMob 金鑰伺服器提供的公開金鑰輪替頻率為何?
AdMob 金鑰伺服器提供的公開金鑰會在變數上輪替 排程。為了確保 SSV 回呼的驗證持續運作, 公開金鑰的快取時間不應超過 24 小時。
如果無法連上我的伺服器,會發生什麼情況?
Google 預期的 HTTP 200 OK 成功狀態回應代碼為 SSV 回呼函式。如果無法連線至伺服器,或是伺服器未提供 回應,Google 會在 間隔一秒。
如何驗證 SSV 回呼是否來自 Google?
使用反向 DNS 查詢,驗證 SSV 回呼是否來自 Google。