Callbacks für serverseitige Überprüfungen validieren

Callbacks für die serverseitige Überprüfung sind URL-Anfragen, bei denen Suchparameter von Google erweitert werden. Sie werden von Google an ein externes System gesendet, um zu informieren, dass ein Nutzer für die Interaktion mit einer Anzeige mit Prämie oder einer Anzeige mit Prämie belohnt werden sollte. Callbacks für serverseitige Überprüfungen mit Prämie bieten einen zusätzlichen Schutz vor Spoofing durch clientseitige Callbacks, um Nutzer zu belohnen.

In diesem Leitfaden wird beschrieben, wie Sie Callbacks für Anzeigen mit Prämie mithilfe der kryptografischen Bibliothek der Tink Java Apps von Drittanbietern prüfen, um sicherzustellen, dass die Abfrageparameter im Callback legitime Werte sind. Tink wird in diesem Leitfaden zwar verwendet, Sie können aber jede beliebige Drittanbieterbibliothek verwenden, die ECDSA unterstützt. Sie können Ihren Server auch mit dem Testtool in der AdMob-Benutzeroberfläche testen.

Sehen Sie sich dieses vollständig funktionsfähige Beispiel mit Java-Spring-Boot an.

Voraussetzungen

Anzeige mit Prämie aus der Tink Java Apps-Bibliothek verwenden

Das Tink Java Apps-GitHub-Repository enthält eine RewardedAdsVerifier-Hilfsklasse zur Reduzierung des Codes, der zum Überprüfen eines SSV-Callbacks mit Prämie erforderlich ist. Mit dieser Klasse können Sie eine Callback-URL mit dem folgenden Code bestätigen.

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

Wenn die Methode verify() ohne Ausnahme ausgeführt wird, wurde die Callback-URL erfolgreich bestätigt. Im Abschnitt Prämie finden Sie Best Practices, wann Nutzer belohnt werden sollten. Eine Aufschlüsselung der Schritte dieser Klasse zur Überprüfung von Callbacks mit Prämie für SSV finden Sie im Abschnitt Manuelle Überprüfung von Prämien für SSVs.

SSV-Callback-Parameter

Callbacks für die serverseitige Überprüfung enthalten Abfrageparameter, die die Anzeigeninteraktion mit Prämie beschreiben. Parameternamen, Beschreibungen und Beispielwerte sind unten aufgeführt. Parameter werden in alphabetischer Reihenfolge gesendet.

Parametername Beschreibung Beispielwert
Werbenetzwerk ID der Anzeigenquelle der Anzeigenquelle, die die Anzeige ausgeliefert hat Die Namen der Anzeigenquelle, die den ID-Werten entsprechen, werden im Abschnitt Kennungen der Anzeigenquelle aufgeführt. 1953547073528090325
Anzeigenblock AdMob-Anzeigenblock-ID, mit der die Anzeige mit Prämie angefordert wurde. 2747237135
Benutzerdefinierte Daten Benutzerdefinierter Datenstring von setCustomData .

Wenn die Anwendung keinen benutzerdefinierten Datenstring enthält, ist dieser Abfrageparameterwert im SSV-Callback nicht vorhanden.

BEISPIEL_BENUTZERDEFINIERTE_DATEN_STRING
Schlüssel-ID Schlüssel zur Bestätigung des SSV-Callbacks. Dieser Wert ist einem öffentlichen Schlüssel zugeordnet, der vom AdMob-Schlüsselserver bereitgestellt wird. 1234567890
Prämienbetrag Prämienbetrag, der in den Einstellungen für den Anzeigenblock angegeben ist. 5
Prämienartikel Prämie wie in den Einstellungen des Anzeigenblocks angegeben. Münzen
Signatur Signatur für SSV-Callbacks von AdMob. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
timestamp Zeitstempel für die Zeit, in der der Nutzer als Epochenzeit in ms belohnt wurde. 1507770365237823
transaction_id Eindeutige Hexadezimal-Kennung für jedes von AdMob generierte Prämienereignis. 18fa792de1bca816048293fc71035638
user_id Nutzerkennung wie vonsetUserId angegeben.

Wenn die Anwendung keine Nutzerkennung bereitstellt, ist dieser Abfrageparameter nicht im SSV-Callback vorhanden.

1234567

Anzeigenquellen-IDs

Namen und IDs von Anzeigenquellen

Name der Anzeigenquelle ID der Anzeigenquelle
Aarki (Gebote)5240798063227064260
Anzeigengenerierung (Bidding)1477265452970951479
AdColony15586990674969969776
AdColony (ohne SDK) (Bidding)4600416542059544716
AdColony (Bidding)6895345910719072481
AdFalcon3528208921554210682
AdMob-Werbenetzwerk5450213213286189855
Anzeigenergebnis10593873382626181482
AMoAd17253994435944008978
AppLovin1063618907739174004
Applovin (Bidding)1328079684332308356
Chartboost2873236629771172317
Schokoladenplattform (Bidding)6432849193975106527
Kanalübergreifend (MdotM)9372067028804390441
Benutzerdefiniertes Ereignis18351550913290782395
DT Exchange*
* Vor dem 21. September 2022 hieß dieses Netzwerk „Fyber Marketplace“.
2179455223494392917
Fluktuation (Gebote)8419777862490735710
Schneegestöber3376427960656545613
Fyber*
* Diese Anzeigenquelle wird für Berichte zu bisherigen Daten verwendet.
4839637394546996422
i-mobile5208827440166355534
Digitales Bidding verbessern (Bidding)159382223051638006
Index Exchange (Gebote)4100650709078789802
InMobi7681903010231960328
InMobi (Bidding)6325663098072678541
Logo: IronSource6925240245545091930
Leadbolt2899150749497968595
LG U+AD18298738678491729107
maio7505118203095108657
Maio (Bidding)1343336733822567166
Media.net (Gebote)2127936450554446159
Vermittelte hausinterne Anzeigen6060308706800320801
Meta Audience Network*
* Vor dem 6. Juni 2022 wurde das Netzwerk als „Facebook Audience Network“ bezeichnet.
10568273599589928883
Meta Audience Network (Bidding)*
* Vor dem 6. Juni 2022 hieß dieses Netzwerk „Facebook Audience Network (Bidding)“.
11198165126854996598
MobFox8079529624516381459
MoPub (eingestellt)10872986198578383917
meinZiel8450873672465271579
Nend9383070032774777750
ONE von AOL (Millennial Media)6101072188699264581
ONE von AOL (Nexage)3224789793037044399
OpenX (Bidding)4918705482605678398
Pangle (Gebote)3525379893916449117
PubMatic (Gebote)3841544486172445473
Reservierungskampagne7068401028668408324
RhythmOne (Gebote)2831998725945605450
Rubicon (Bidding)3993193775968767067
SK-Planeten734341340207269415
Sharethrough (Gebote)5247944089976324188
Smaato (Bidding)3362360112145450544
Gleichung (Bidding)*

* Vor dem 12. Januar 2023 wurde dieses Netzwerk als „Smart Adserver“ bezeichnet.

5970199210771591442
Sonobi (Gebote)3270984106996027150
Tapjoy7295217276740746030
Tapjoy (Gebote)4692500501762622178
Zehntausende GDT7007906637038700218
TripleLift (Gebote)8332676245392738510
Unity-Anzeigen4970775877303683148
UnrulyX (Bidding)2831998725945605450
Logo von Verizon Media7360851262951344112
VPON1940957084538325905
Liftoff-Monetarisierung*

* Vor dem 30. Januar 2023 hieß dieses Netzwerk „Vungle“.

1953547073528090325
Liftoff-Monetarisierung (Bidding)*

* Vor dem 30. Januar 2023 wurde dieses Netzwerk als „Vungle (Bidding)“ bezeichnet.

4692500501762622185
Yieldmo (Gebote)4193081836471107579
YieldOne (Gebote)3154533971590234104
Zucken5506531810221735863

Prämie für den Nutzer

Es ist wichtig, bei der Entscheidung, wann ein Nutzer belohnt werden soll, ein ausgewogenes Verhältnis zwischen Nutzererfahrung und Validierung der Prämie zu finden. Bei serverseitigen Callbacks kann es zu Verzögerungen kommen, bevor externe Systeme erreicht werden. Daher wird empfohlen, den Nutzer über den clientseitigen Callback sofort zu belohnen und gleichzeitig alle Belohnungen nach Erhalt serverseitiger Callbacks zu validieren. Dieser Ansatz bietet eine gute Nutzererfahrung und gewährleistet gleichzeitig die Gültigkeit der vergebenen Prämien.

Bei Anwendungen, bei denen die Gültigkeit der Prämie entscheidend ist, z. B. die Belohnung der In-Game-Ökonomie Ihrer App, und Verzögerungen bei der Gewährung von Prämien akzeptabel sind, kann es sinnvoll sein, auf den bestätigten serverseitigen Callback zu warten.

Benutzerdefinierte Daten

Apps, die zusätzliche Daten in serverseitigen Callbacks zur Bestätigung benötigen, sollten die Funktion für benutzerdefinierte Daten von Anzeigen mit Prämie verwenden. Jeder Stringwert, der für ein Anzeigenobjekt mit Prämie festgelegt ist, wird an den Abfrageparameter custom_data des SSV-Callbacks übergeben. Wenn kein benutzerdefinierter Datenwert festgelegt ist, ist der Wert des Abfrageparameters custom_data nicht im SSV-Callback enthalten.

Das folgende Codebeispiel zeigt, wie Sie die SSV-Optionen festlegen, nachdem die Anzeige mit Prämie geladen wurde.

Java

RewardedAd.load(MainActivity.this, "ca-app-pub-3940256099942544/5354046379",
    new AdRequest.Builder().build(),  new RewardedAdLoadCallback() {
  @Override
  public void onAdLoaded(RewardedAd ad) {
    Log.d(TAG, "Ad was loaded.");
    rewardedAd = ad;
    ServerSideVerificationOptions options = new ServerSideVerificationOptions
        .Builder()
        .setCustomData("SAMPLE_CUSTOM_DATA_STRING")
        .build();
    rewardedAd.setServerSideVerificationOptions(options);
  }
  @Override
  public void onAdFailedToLoad(LoadAdError loadAdError) {
    Log.d(TAG, loadAdError.toString());
    rewardedAd = null;
  }
});

Kotlin

RewardedAd.load(this, "ca-app-pub-3940256099942544/5354046379",
    AdRequest.Builder().build(), object : RewardedAdLoadCallback() {
  override fun onAdLoaded(ad: RewardedAd) {
    Log.d(TAG, "Ad was loaded.")
    rewardedInterstitialAd = ad
    val options = ServerSideVerificationOptions.Builder()
        .setCustomData("SAMPLE_CUSTOM_DATA_STRING")
        .build()
    rewardedAd.setServerSideVerificationOptions(options)
  }

  override fun onAdFailedToLoad(adError: LoadAdError) {
    Log.d(TAG, adError?.toString())
    rewardedAd = null
  }
})

Wenn Sie den String für eine benutzerdefinierte Prämie festlegen möchten, müssen Sie das tun, bevor die Anzeige ausgeliefert wird.

Manuelle Überprüfung von Anzeigen mit Prämie

Die Schritte, die von der Klasse RewardedAdsVerifier zur Überprüfung einer SSV mit Prämie ausgeführt werden, sind weiter unten aufgeführt. Obwohl die enthaltenen Code-Snippets in Java enthalten sind und die Tink-Drittanbieterbibliothek nutzen, können Sie diese Schritte mit einer beliebigen Drittanbieterbibliothek, die ECDSA unterstützt, in der Sprache Ihrer Wahl implementieren.

Öffentliche Schlüssel abrufen

Wenn Sie einen SSV-Callback bestätigen möchten, benötigen Sie einen öffentlichen Schlüssel von AdMob.

Eine Liste der öffentlichen Schlüssel, die zum Validieren von SSV-Callbacks verwendet werden, kann vom AdMob-Schlüsselserver abgerufen werden. Die Liste der öffentlichen Schlüssel wird als JSON-Darstellung in einem Format wie dem folgenden zur Verfügung gestellt:

{
 "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=="
    },
  ],
}

Stellen Sie eine Verbindung zum AdMob-Schlüsselserver her und laden Sie die Schlüssel herunter, um die öffentlichen Schlüssel abzurufen. Mit dem folgenden Code wird diese Aufgabe ausgeführt und die JSON-Darstellung der Schlüssel in der Variablen data gespeichert.

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();
}

Beachten Sie, dass öffentliche Schlüssel regelmäßig rotiert werden. Sie werden per E-Mail über eine anstehende Rotation informiert. Wenn Sie öffentliche Schlüssel im Cache speichern, sollten Sie die Schlüssel nach Erhalt dieser E-Mail aktualisieren.

Nachdem die öffentlichen Schlüssel abgerufen wurden, müssen sie geparst werden. Die Methode parsePublicKeysJson verwendet einen JSON-String wie im Beispiel oben und erstellt eine Zuordnung von key_id-Werten zu öffentlichen Schlüsseln, die als ECPublicKey-Objekte aus der Tink-Bibliothek gekapselt werden.

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;
}

Inhalte zur Bestätigung abrufen

Die letzten beiden Abfrageparameter von SSV-Rückrufen mit Prämie sind immer signature und key_id, in dieser Reihenfolge. Die verbleibenden Abfrageparameter geben den zu überprüfenden Inhalt an. Angenommen, Sie haben AdMob-Callbacks für Prämien an https://www.myserver.com/mypath konfiguriert. Das folgende Snippet zeigt ein Beispiel für einen SSV-Callback mit den zu bestätigenden Inhalten.

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

Der folgende Code zeigt, wie der Inhalt, der anhand einer Callback-URL überprüft werden soll, als UTF-8-Byte-Array geparst wird.

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

Signatur und key_id von Callback-URL abrufen

Parst mit dem Wert queryString aus dem vorherigen Schritt die Abfrageparameter signature und key_id aus der Callback-URL, wie unten dargestellt:

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

Bestätigung durchführen

Im letzten Schritt wird der Inhalt der Callback-URL mit dem entsprechenden öffentlichen Schlüssel geprüft. Verwenden Sie die von der Methode parsePublicKeysJson zurückgegebene Zuordnung und verwenden Sie den Parameter key_id aus der Callback-URL, um den öffentlichen Schlüssel aus dieser Zuordnung abzurufen. Bestätigen Sie dann die Signatur mit diesem öffentlichen Schlüssel. Diese Schritte werden unten in der Methode verify beschrieben.

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

Wenn die Methode ohne Ausnahme ausgeführt wird, wurde die Callback-URL erfolgreich geprüft.

FAQ

Kann ich den vom AdMob-Schlüsselserver bereitgestellten öffentlichen Schlüssel im Cache speichern?
Wir empfehlen, den vom AdMob-Schlüsselserver bereitgestellten öffentlichen Schlüssel im Cache zu speichern, um die Anzahl der Vorgänge zu reduzieren, die zum Validieren von SSV-Callbacks erforderlich sind. Beachten Sie jedoch, dass öffentliche Schlüssel regelmäßig rotiert werden und nicht länger als 24 Stunden im Cache gespeichert werden sollen.
Wie oft werden die vom AdMob-Schlüsselserver bereitgestellten öffentlichen Schlüssel rotiert?
Öffentliche Schlüssel, die vom AdMob-Schlüsselserver bereitgestellt werden, werden in einem variablen Zeitplan rotiert. Damit die Bestätigung von SSV-Callbacks weiterhin wie vorgesehen funktioniert, sollten öffentliche Schlüssel nicht länger als 24 Stunden im Cache gespeichert werden.
Was passiert, wenn mein Server nicht erreichbar ist?
Google erwartet einen Antwortcode zum Erfolgsstatus HTTP 200 OK für SSV-Callbacks. Wenn Ihr Server nicht erreicht werden kann oder nicht die erwartete Antwort liefert, versucht Google, SSV-Callbacks bis zu fünfmal im Sekundentakt zu senden.
Wie kann ich prüfen, ob SSV-Callbacks von Google stammen?
Mit einem umgekehrten DNS-Lookup prüfen Sie, ob SSV-Callbacks von Google stammen.