Convalidare i callback di verifica lato server (SSV)

I callback di verifica lato server sono richieste URL con parametri di query espanse da Google, inviate da Google a un sistema esterno per comunicagli che un utente deve essere premiato per aver interagito con un premio annuncio interstitial con premio. Callback SSV (verifica lato server) premiato offrono un ulteriore livello di protezione contro lo spoofing dei callback lato client per premiare gli utenti.

Questa guida mostra come verificare i callback della verifica lato server con premio utilizzando il App Java Tink di terze parti libreria crittografica per garantire che i parametri di query nel callback valori legittimi. Anche se Tink è utilizzato ai fini di questa guida, hai la possibilità di utilizzare qualsiasi libreria di terze parti che supporti ECDSA. Puoi anche testare il server con nell'interfaccia utente di AdMob.

Dai un'occhiata a questo video esempio usando Spring-boot Java.

Prerequisiti

Utilizza RewardAdsVerifier dalla raccolta di app Java di Tink

Il repository GitHub delle app Java Tink include un oggetto RewardedAdsVerifier: per ridurre il codice necessario per verificare un callback di SSV con premio. Questa classe ti consente di verificare un URL di callback con il seguente codice.

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

Se il metodo verify() viene eseguito senza generare un'eccezione, il callback L'URL è stato verificato. L'opzione Ricompensare l'utente descrive in dettaglio le best practice relative a quando gli utenti devono essere premiati. Per un dei passaggi eseguiti da questa classe per verificare i callback della verifica lato server con premio puoi leggere le istruzioni della verifica manuale degli annunci .

Parametri callback SSV

I callback di verifica lato server contengono parametri di query che descrivono interazione con l'annuncio con premio. Nomi dei parametri, descrizioni e valori di esempio sono elencati di seguito. I parametri vengono inviati in ordine alfabetico.

Nome parametro Descrizione Valore di esempio
ad_network Identificatore dell'origine annuncio che ha pubblicato questo annuncio. Origine annuncio nomi corrispondenti ai valori ID sono indicati nella scheda identificatori di origine. 1953547073528090325
ad_unit L'ID unità pubblicitaria AdMob utilizzato per richiedere l'annuncio con premio. 2747237135
custom_data Stringa di dati personalizzata fornita da customRewardString .

Se l'app non fornisce alcuna stringa di dati personalizzata, questo parametro di query non sarà presente nel callback SSV.

SAMPLE_CUSTOM_DATA_STRING
key_id Chiave da utilizzare per verificare il callback SSV. Questo valore è mappato a una chiave pubblica forniti dal server delle chiavi AdMob. 1234567890
reward_amount Importo del premio specificato nelle impostazioni dell'unità pubblicitaria. 5
reward_item Premio come specificato nelle impostazioni dell'unità pubblicitaria. monete
firma Firma per il callback della verifica lato client generato da AdMob. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
timestamp Timestamp del momento in cui l'utente è stato premiato come tempo dell'epoca in ms. 1507770365237823
transaction_id Identificatore univoco codificato esadecimale per ogni evento di concessione di premi generato da AdMob. 18fa792de1bca816048293fc71035638
user_id Identificatore utente fornito da userIdentifier

Se l'app non fornisce alcun identificatore utente, questo parametro di query non nel callback SSV.

1234567

Identificatori delle origini annuncio

Nomi e ID delle origini annuncio

Ad source name Ad source ID
Aarki (bidding)5240798063227064260
Ad Generation (bidding)1477265452970951479
AdColony15586990674969969776
AdColony (non-SDK) (bidding)4600416542059544716
AdColony (bidding)6895345910719072481
AdFalcon3528208921554210682
AdMob Network5450213213286189855
AdMob Network Waterfall1215381445328257950
ADResult10593873382626181482
AMoAd17253994435944008978
Applovin1063618907739174004
Applovin (bidding)1328079684332308356
Chartboost2873236629771172317
Chocolate Platform (bidding)6432849193975106527
CrossChannel (MdotM)9372067028804390441
Custom Event18351550913290782395
DT Exchange*
* Prior to September 21, 2022, this network was called "Fyber Marketplace".
2179455223494392917
EMX (bidding)8497809869790333482
Fluct (bidding)8419777862490735710
Flurry3376427960656545613
Fyber*
* This ad source is used for historical reporting.
4839637394546996422
i-mobile5208827440166355534
Improve Digital (bidding)159382223051638006
Index Exchange (bidding)4100650709078789802
InMobi7681903010231960328
InMobi (bidding)6325663098072678541
InMobi Exchange (bidding)5264320421916134407
IronSource6925240245545091930
ironSource Ads (bidding)1643326773739866623
Leadbolt2899150749497968595
LG U+AD18298738678491729107
LINE Ads Network3025503711505004547
maio7505118203095108657
maio (bidding)1343336733822567166
Media.net (bidding)2127936450554446159
Mediated house ads6060308706800320801
Meta Audience Network*
* Prior to June 6, 2022, this network was called "Facebook Audience Network".
10568273599589928883
Meta Audience Network (bidding)*
* Prior to June 6, 2022, this network was called "Facebook Audience Network (bidding)".
11198165126854996598
Mintegral1357746574408896200
Mintegral (bidding)6250601289653372374
MobFox8079529624516381459
MobFox (bidding)3086513548163922365
MoPub (deprecated)10872986198578383917
myTarget8450873672465271579
Nend9383070032774777750
Nexxen (bidding)*

* Prior to May 1, 2024, this network was called "UnrulyX".

2831998725945605450
ONE by AOL (Millennial Media)6101072188699264581
ONE by AOL (Nexage)3224789793037044399
OneTag Exchange (bidding)4873891452523427499
OpenX (bidding)4918705482605678398
Pangle4069896914521993236
Pangle (bidding)3525379893916449117
PubMatic (bidding)3841544486172445473
Reservation campaign7068401028668408324
RhythmOne (bidding)2831998725945605450
Rubicon (bidding)3993193775968767067
SK planet734341340207269415
Sharethrough (bidding)5247944089976324188
Smaato (bidding)3362360112145450544
Equativ (bidding)*

* Prior to January 12, 2023, this network was called "Smart Adserver".

5970199210771591442
Sonobi (bidding)3270984106996027150
Tapjoy7295217276740746030
Tapjoy (bidding)4692500501762622178
Tencent GDT7007906637038700218
TripleLift (bidding)8332676245392738510
Unity Ads4970775877303683148
Unity Ads (bidding)7069338991535737586
Verizon Media7360851262951344112
Verve Group (bidding)5013176581647059185
Vpon1940957084538325905
Liftoff Monetize*

* Prior to January 30, 2023, this network was called "Vungle".

1953547073528090325
Liftoff Monetize (bidding)*

* Prior to January 30, 2023, this network was called "Vungle (bidding)".

4692500501762622185
Yieldmo (bidding)4193081836471107579
YieldOne (bidding)3154533971590234104
Zucks5506531810221735863

Ricompensare l'utente

Al momento di decidere, è importante trovare un equilibrio tra l'esperienza utente e la convalida dei premi quando premiare un utente. Le callback lato server potrebbero subire ritardi prima che raggiungono i sistemi esterni. Pertanto, la best practice consigliata è utilizzare il callback lato client per ricompensare immediatamente l'utente mentre esegue la convalida di tutti i premi al ricevimento dei callback lato server. Questo offre una buona esperienza utente garantendo al contempo la validità premi.

Tuttavia, per le richieste in cui la validità del premio è fondamentale (ad esempio, premio incide sull'economia in-game della tua app), mentre i ritardi nella concessione dei premi sono accettabile, l'attesa della richiamata lato server verificata potrebbe essere la scelta migliore l'importanza di un approccio umile.

Dati personalizzati

Le app che richiedono dati aggiuntivi nei callback di verifica lato server devono utilizzare la funzionalità dei dati personalizzati degli annunci con premio. Qualsiasi valore di stringa impostato su un annuncio con premio viene passato al parametro di query custom_data del callback SSV. In caso contrario valore dei dati personalizzati è impostato, il valore del parametro di ricerca custom_data non sarà presente nel callback SSV.

Il seguente esempio di codice mostra come impostare le opzioni SSV dopo il comando l'annuncio con premio è stato caricato.

Swift

GADRewardedAd.load(withAdUnitID:"ca-app-pub-3940256099942544/1712485313",
                       request: request,
                       completionHandler: { [self] ad, error in
      if let error != error {
      rewardedAd = ad
      let options = GADServerSideVerificationOptions()
      options.customRewardString = "SAMPLE_CUSTOM_DATA_STRING"
      rewardedAd.serverSideVerificationOptions = options
    }

Objective-C

GADRequest *request = [GADRequest request];
[GADRewardedAd loadWithAdUnitID:@"ca-app-pub-3940256099942544/1712485313"
                        request:request
              completionHandler:^(GADRewardedAd *ad, NSError *error) {
                if (error) {
                  // Handle Error
                  return;
                }
                self.rewardedAd = ad;
                GADServerSideVerificationOptions *options =
                    [[GADServerSideVerificationOptions alloc] init];
                options.customRewardString = @"SAMPLE_CUSTOM_DATA_STRING";
                ad.serverSideVerificationOptions = options;
              }];

Verifica manuale della verifica lato server con premio

I passaggi eseguiti dalla classe RewardedAdsVerifier per verificare un premio la verifica lato server è descritta di seguito. Anche se gli snippet di codice inclusi sono in Java usare la libreria di terze parti Tink, puoi implementare questi passaggi la lingua che preferisci, utilizzando qualsiasi libreria di terze parti che supporti ECDSA.

Recupera chiavi pubbliche

Per verificare il callback della verifica lato server con premio, devi avere una chiave pubblica fornita da AdMob.

È possibile utilizzare un elenco di chiavi pubbliche da utilizzare per convalidare i callback della verifica lato server con premio recuperata dalla chiave AdMob o server web. L'elenco delle chiavi pubbliche viene fornito come rappresentazione JSON con un formato simile al seguente:

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

Per recuperare le chiavi pubbliche, collegati al server delle chiavi AdMob e scarica chiave. Il codice seguente esegue questa attività e salva il file JSON delle chiavi alla variabile 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();
}

Tieni presente che le chiavi pubbliche vengono ruotate regolarmente. Riceverai un'email per informarti di una rotazione imminente. Se stai memorizzando nella cache le chiavi pubbliche, devi aggiornare le chiavi alla ricezione di questa email.

Una volta recuperate, le chiavi pubbliche devono essere analizzate. La Il metodo parsePublicKeysJson seguente utilizza una stringa JSON, come quella dell'esempio sopra, come input, e crea una mappatura dai valori key_id alle chiavi pubbliche, che sono incapsulati come oggetti ECPublicKey dalla libreria 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;
}

Richiedi la verifica dei contenuti

Gli ultimi due parametri di query dei callback SSV con premio sono sempre signature e key_id, in quell'ordine. I restanti parametri di query specificano i contenuti da verificare. Supponiamo che tu abbia configurato AdMob per l'invio dei callback dei premi a https://www.myserver.com/mypath. Lo snippet seguente mostra un esempio di premio Callback SSV con i contenuti da verificare evidenziati.

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

Il codice seguente mostra come analizzare i contenuti da verificare da un come un array di byte 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"));

Ottieni la firma e il key_id dall'URL di callback

Utilizzando il valore queryString del passaggio precedente, analizza i signature e key_id parametri di query dall'URL di callback, come mostrato di seguito:

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

Esegui la verifica

Il passaggio finale consiste nel verificare i contenuti dell'URL di callback con il parametro chiave pubblica appropriata. Prendi il mapping restituito dal parsePublicKeysJson e usa il parametro key_id del callback URL per ottenere la chiave pubblica dal mapping. Poi verifica la firma con della chiave pubblica. Questi passaggi sono mostrati di seguito nel metodo 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);
  }
}

Se il metodo viene eseguito senza generare un'eccezione, l'URL di callback verificato correttamente.

Domande frequenti

Posso memorizzare nella cache la chiave pubblica fornita dal server della chiave AdMob?
Ti consigliamo di memorizzare nella cache la chiave pubblica fornita dalla chiave AdMob. per ridurre il numero di operazioni necessarie per convalidare la verifica lato server i callback. Tuttavia, tieni presente che le chiavi pubbliche vengono ruotate regolarmente e non devono essere memorizzate nella cache per più di 24 ore.
Con quale frequenza vengono ruotate le chiavi pubbliche fornite dal server delle chiavi AdMob?
Le chiavi pubbliche fornite dal server chiavi di AdMob vengono ruotate in base a una variabile programmazione. Per garantire che la verifica dei callback di SSV continui a funzionare come le chiavi pubbliche non devono essere memorizzate nella cache per più di 24 ore.
Cosa succede se il mio server non è raggiungibile?
Google si aspetta un codice di risposta relativo allo stato di operazione riuscita per HTTP 200 OK i callback. Se non è possibile raggiungere il server o non fornisce la risposta prevista risposta, Google tenterà nuovamente di inviare i callback SSV fino a cinque volte in intervalli di 1 secondo.
Come faccio a verificare che i callback di SSV provengano da Google?
Utilizza la ricerca DNS inversa per verificare che i callback SSV provengano da Google.