Valider les rappels de vérification côté serveur (SSV)

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Les rappels de validation côté serveur sont des demandes d'URL, avec des paramètres de requête développés par Google, qui sont envoyées par Google à un système externe pour l'avertir qu'un utilisateur doit être récompensé pour avoir interagi avec une annonce interstitielle avec récompense ou avec récompense. Les rappels SSV (validation côté serveur) offrent une protection supplémentaire contre le spoofing de rappels côté client pour récompenser les utilisateurs.

Ce guide explique comment valider les rappels SSV avec récompense à l'aide de la bibliothèque cryptographique tierce Tink pour vous assurer que les paramètres de requête du rappel sont des valeurs légitimes. Bien que Tink soit utilisé dans ce guide, vous avez la possibilité d'utiliser n'importe quelle bibliothèque tierce compatible avec ECDSA. Vous pouvez également tester votre serveur avec l'outil de test dans l'interface utilisateur AdMob.

Consultez cet exemple fonctionnel utilisant le ressort de Java.

Prérequis

Utiliser RécompenseAdsAdsVerifier

Le dépôt GitHub Tink inclut une classe d'assistance RewardedAdsVerifier permettant de réduire le code requis pour valider un rappel SSV avec récompense. En l'utilisant conjointement avec la bibliothèque cryptographique tierce Tink, vous pouvez valider une URL de rappel à l'aide du code suivant.

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

Si la méthode verify() s'exécute sans générer d'exception, l'URL de rappel a bien été validée. La section Récompenser l'utilisateur détaille les bonnes pratiques à respecter pour être récompensé. Pour découvrir en détail la procédure à suivre pour vérifier les rappels SSV avec récompense, vous pouvez consulter la section Validation manuelle des rappels SSV avec récompense.

Paramètres de rappel SSV

Les rappels de validation côté serveur contiennent des paramètres de requête qui décrivent l'interaction avec l'annonce avec récompense. Les noms, les descriptions et les exemples de valeurs des paramètres sont listés ci-dessous. Les paramètres sont envoyés par ordre alphabétique.

Nom du paramètre Description Exemple de valeur
ad_network (réseau publicitaire) Identifiant de la source d'annonce ayant diffusé cette annonce. Les noms de sources d'annonces correspondant aux valeurs d'ID sont listés dans la section Identifiants de sources d'annonces. 1953547073528090325
bloc_annonces ID du bloc d'annonces AdMob utilisé pour demander l'annonce avec récompense. 2747237135
données_personnalisées Chaîne de données personnalisée fournie par setCustomData .

Si l'application ne fournit aucune chaîne de données personnalisée, la valeur de ce paramètre de requête ne sera pas présente dans le rappel SSV.

SAMPLE_CUSTOM_DATA_STRING
id_clé Clé à utiliser pour valider le rappel SSV. Cette valeur correspond à une clé publique fournie par le serveur de clés AdMob. 1234567890
prime_amount [montant_remise] Montant de la récompense tel que spécifié dans les paramètres du bloc d'annonces. 5
récompense_article Récompense comme indiqué dans les paramètres du bloc d'annonces. pièces
signature Signature pour le rappel SSV généré par AdMob. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY (TRANSLATED)
timestamp Horodatage correspondant à la période pendant laquelle l'utilisateur a obtenu sa récompense (temps passé en ms). 1507770365237823
transaction_id Identifiant unique encodé en hexadécimal pour chaque événement de récompense généré par AdMob. 18fa792fr1bca816048293fc71035638
user_id Identifiant utilisateur fourni par setUserId.

Si aucun identifiant utilisateur n'est fourni par l'application, ce paramètre de requête ne sera pas inclus dans le rappel SSV.

1234567

Identifiants des sources d'annonces

ID et noms des sources d'annonces

Nom de la source de l'annonce ID de la source de l'annonce
Aarki (enchères)5240798063227064260
Génération d'annonces (enchères)1477265452970951479
AdColony15586990674969969776
AdColony (sans SDK) (enchères)4600416542059544716
AdColony (enchères)6895345910719072481
AdFalcon3528208921554210682
Réseau AdMob5450213213286189855
Résultat AD10593873382626181482
AMoAd17253994435944008978
Applovine1063618907739174004
Applovine (enchères)1328079684332308356
Chartboost2873236629771172317
Chocolate Platform (enchères)6432849193975106527
Multicanal (MdotM)9372067028804390441
Événement personnalisé18351550913290782395
transfert de données
Avant le 21 septembre 2022, ce réseau s'appelait "Fyber".
4839637394546996422
Fluctuation (enchères)8419777862490735710
Bourrasque3376427960656545613
i-mobile5208827440166355534
Improve Digital (enchères)159382223051638006
Index Exchange (enchères)4100650709078789802
InMobi7681903010231960328
InMobi (enchères)6325663098072678541
Source de fer6925240245545091930
Leadbolt2899150749497968595
LG U+AD18298738678491729107
maio7505118203095108657
Maio (enchères)1343336733822567166
Media.net (enchères)2127936450554446159
Auto-promotions par médiation6060308706800320801
Meta Audience Network
Avant le 6 juin 2022, ce réseau s'appelait le "Facebook Audience Network".
10568273599589928883
Meta Audience Network (enchères)
Avant le 6 juin 2022, ce réseau était appelé "Facebook Audience Network (enchères)".
11198165126854996598
MobFox8079529624516381459
MoPub (obsolète)10872986198578383917
myTarget8450873672465271579
Nend9383070032774777750
ONE by AOL (Millennial Media)6101072188699264581
ONE by AOL (Nexage)3224789793037044399
OpenX (enchères)4918705482605678398
Pangle (enchères)3525379893916449117
PubMatic (enchères)3841544486172445473
Campagne par réservation7068401028668408324
RhythmOne (enchères)2831998725945605450
Rubicon (enchères)3993193775968767067
Planète SK734341340207269415
Smaato (enchères)3362360112145450544
Tapjoy7295217276740746030
Tapjoy (enchères)4692500501762622178
Tencent GDT7007906637038700218
TripleLift (enchères)8332676245392738510
Unity Ads4970775877303683148
Verizon Media7360851262951344112
Vpon1940957084538325905
Vungle1953547073528090325
Vungle (enchères)4692500501762622185
Zucks5506531810221735863

Récompenser l'utilisateur

Il est important de trouver un juste équilibre entre l'expérience utilisateur et la validation des récompenses lorsque vous décidez de les récompenser. Les rappels côté serveur peuvent subir des retards avant d'atteindre les systèmes externes. Par conséquent, nous vous recommandons d'utiliser le rappel côté client pour récompenser immédiatement l'utilisateur, tout en effectuant la validation de toutes les récompenses à la réception de rappels côté serveur. Cette approche offre une bonne expérience utilisateur tout en garantissant la validité des récompenses accordées.

Toutefois, si la validité de la récompense est essentielle (par exemple, si la récompense a une incidence sur l'économie du jeu) et que les délais d'octroi de récompenses sont acceptables, il est préférable d'attendre le rappel validé côté serveur.

Données personnalisées

Les applications qui nécessitent des données supplémentaires dans les rappels de validation côté serveur doivent utiliser la fonctionnalité de données personnalisées des annonces avec récompense. Toute valeur de chaîne définie sur un objet d'annonce avec récompense est transmise au paramètre de requête custom_data du rappel SSV. Si aucune valeur de données personnalisée n'est définie, la valeur du paramètre de requête custom_data ne sera pas présente dans le rappel SSV.

L'exemple de code suivant montre comment définir les options SSV après le chargement de l'annonce avec récompense.

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

Si vous souhaitez définir la chaîne de récompenses personnalisée, vous devez le faire avant de diffuser l'annonce.

Validation manuelle des SSV avec récompense

Les étapes effectuées par la classe RewardedAdsVerifier pour vérifier une SSV avec récompense sont décrites ci-dessous. Bien que les extraits de code inclus soient en Java et exploitent la bibliothèque tierce Tink, vous pouvez implémenter ces étapes dans le langage de votre choix à l'aide d'une bibliothèque tierce compatible avec ECDSA.

Récupérer les clés publiques

Pour valider un rappel SSV avec récompense, vous avez besoin d'une clé publique fournie par AdMob.

La liste des clés publiques à utiliser pour valider les rappels SSV avec récompense peut être extraite du serveur de clés AdMob. La liste des clés publiques est fournie sous la forme d'une représentation JSON au format suivant:

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

Pour récupérer les clés publiques, connectez-vous au serveur de clés AdMob et téléchargez-les. Le code suivant accomplit cette tâche et enregistre la représentation JSON des clés dans la variable 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();
}

Notez que les clés publiques sont alternées régulièrement. Vous recevrez un e-mail vous informant d'une rotation à venir. Si vous mettez en cache des clés publiques, vous devez les mettre à jour à la réception de cet e-mail.

Une fois les clés publiques récupérées, elles doivent être analysées. La méthode parsePublicKeysJson ci-dessous prend comme entrée une chaîne JSON, comme dans l'exemple ci-dessus, et crée un mappage des valeurs key_id aux clés publiques, qui sont encapsulées en tant qu'objets ECPublicKey à partir de la bibliothèque Tink.

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

Obtenir le contenu à valider

Les deux derniers paramètres de rappel des SSV avec récompense sont toujours signature et key_id, dans cet ordre. Les paramètres de requête restants spécifient le contenu à vérifier. Supposons que vous ayez configuré AdMob pour qu'il envoie des rappels de récompense à https://www.myserver.com/mypath. L'extrait ci-dessous montre un exemple de rappel SSV avec récompense, avec le contenu à valider en surbrillance.

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

Le code ci-dessous montre comment analyser le contenu à vérifier à partir d'une URL de rappel en tant que tableau d'octets 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"));

Obtenir la signature et le key_id à partir de l'URL de rappel

À l'aide de la valeur queryString de l'étape précédente, analysez les paramètres de requête signature et key_id à partir de l'URL de rappel, comme indiqué ci-dessous:

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

Effectuer la validation

La dernière étape consiste à vérifier le contenu de l'URL de rappel avec la clé publique appropriée. Prenez le mappage renvoyé par la méthode parsePublicKeysJson et utilisez le paramètre key_id de l'URL de rappel pour obtenir la clé publique à partir de ce mappage. Vérifiez ensuite la signature avec cette clé publique. Ces étapes sont illustrées ci-dessous dans la méthode 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);
  }
}

Si la méthode s'exécute sans exception, l'URL de rappel a bien été validée.

Questions fréquentes

Puis-je mettre en cache la clé publique fournie par le serveur de clés AdMob ?
Nous vous recommandons de mettre en cache la clé publique fournie par le serveur de clés AdMob afin de réduire le nombre d'opérations nécessaires pour valider les rappels SSV. Notez toutefois que les clés publiques sont alternées régulièrement et ne doivent pas être mises en cache pendant plus de 24 heures.
À quelle fréquence les clés publiques fournies par le serveur de clés AdMob sont-elles alternées ?
Les clés publiques fournies par le serveur de clés AdMob sont alternées selon une programmation variable. Pour que la validation des rappels SSV continue de fonctionner comme prévu, les clés publiques ne doivent pas être mises en cache pendant plus de 24 heures.
Que se passe-t-il si je ne parviens pas à joindre mon serveur ?
Google attend un code de réponse d'état HTTP 200 OK pour les rappels SSV. Si votre serveur est inaccessible ou ne fournit pas la réponse attendue, Google tente à nouveau d'envoyer des rappels SSV jusqu'à cinq fois par intervalles d'une seconde.
Comment vérifier que les rappels SSV proviennent de Google ?
Utilisez la résolution DNS inverse pour vérifier que les rappels SSV proviennent de Google.