Weryfikowanie wywołań zwrotnych po stronie serwera (SSV)

Wywołania zwrotne w ramach weryfikacji po stronie serwera to żądania adresów URL z poszerzonymi parametrami zapytań, które są wysyłane przez Google do systemów zewnętrznych i informują nas, że użytkownik powinien otrzymać nagrodę za interakcję z reklamą pełnoekranową z nagrodą. Wywołania zwrotne z nagrodą SSV (weryfikacja po stronie serwera) stanowią dodatkową warstwę ochrony przed podszywaniem się pod klienta po stronie klienta, aby nagradzać użytkowników.

W tym przewodniku pokazujemy, jak zweryfikować wywołania zwrotne SSV z nagrodą za pomocą biblioteki kryptograficznej Tink Java Apps, aby zapewnić poprawne parametry zapytania w wywołaniach zwrotnych. Mimo że na potrzeby tego przewodnika do tego celu używany jest Tink, masz możliwość korzystania z dowolnej biblioteki zewnętrznej, która obsługuje ECDSA. Możesz też przetestować serwer za pomocą narzędzia do testowania w interfejsie AdMob.

Zapoznaj się z tym w pełni działającym przykładem wykorzystującym sprężynowe oprogramowanie Java.

Wymagania wstępne

Używanie funkcji RewardedAdsVerifier z biblioteki aplikacji Tink Java

Repozytorium Tink Java Apps zawiera klasę pomocniczą RewardedAdsVerifier umożliwiającą zmniejszenie kodu wymaganego do wywołania wywołania zwrotnego reklamy z nagrodą. Użycie tej klasy pozwala zweryfikować adres URL wywołania zwrotnego o poniższym kodzie.

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

Jeśli metoda verify() jest uruchamiana bez zgłaszania wyjątku, adres URL wywołania zwrotnego został zweryfikowany. Sekcja Nagradzanie użytkowników zawiera sprawdzone metody dotyczące tego, kiedy użytkownicy powinni otrzymać nagrodę. Zestawienie działań wykonanych w ramach tej weryfikacji pozwala zweryfikować wywołania zwrotne SSV z nagrodą. Możesz zapoznać się z sekcją Weryfikacja ręczna z nagrodą SSV.

Parametry wywołania zwrotnego SSV

Wywołania zwrotne w ramach weryfikacji po stronie serwera zawierają parametry zapytania opisujące interakcję z reklamą. Nazwy i opisy parametrów oraz przykładowe wartości są podane poniżej. Parametry są przesyłane w kolejności alfabetycznej.

Nazwa parametru opis, Przykładowa wartość
sieć_reklamowa Identyfikator źródła reklamy, który ją wyświetlił. Nazwy źródeł reklam odpowiadające wartościom identyfikatora znajdziesz w sekcji Identyfikatory źródeł reklam. 1953547073528090325
ad_unit, Identyfikator jednostki reklamowej AdMob użyty do wysłania żądania reklamy z nagrodą. 2747237135
dane_niestandardowe Niestandardowy ciąg danych podany przez setCustomData .

Jeśli aplikacja nie poda żadnego niestandardowego ciągu danych, ta wartość parametru zapytania nie będzie występowała w wywołaniu zwrotnym SSV.

SAMPLE_CUSTOM_DATA_STRING
identyfikator_klucza Klucz używany do weryfikowania wywołania zwrotnego SSV. Ta wartość jest mapowana na klucz publiczny udostępniony przez serwer kluczy AdMob. 1234567890
kwota_nagrody Kwota nagrody określona w ustawieniach jednostki reklamowej. 5
element_nagrody Nagroda, zgodnie z ustawieniami jednostki reklamowej. monety
podpis Podpis wywołania zwrotnego SSV wygenerowany przez AdMob. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
sygnatura czasowa Sygnatura czasowa wynagrodzenia użytkownika jako czas epoki w ms. 1507770365237823
transaction_id Unikalny zakodowany szesnastkowo identyfikator każdego zdarzenia przyznania nagrody wygenerowanego przez AdMob. 18fa792de1bca816048293fc71035638
user_id Identyfikator użytkownika podany przez setUserId.

Jeśli aplikacja nie poda identyfikatora użytkownika, ten parametr zapytania nie będzie występował w wywołaniu zwrotnym SSV.

1234567

Identyfikatory źródeł reklam

Nazwy i identyfikatory źródeł reklam

Nazwa źródła reklamy Identyfikator źródła reklamy
Aarki (określanie stawek)5240798063227064260
Generowanie reklam (określanie stawek)1477265452970951479
AdColony15586990674969969776
AdColony (nie SDK) (określanie stawek)4600416542059544716
AdColony (określanie stawek)6895345910719072481
AdFalcon3528208921554210682
Sieć AdMob5450213213286189855
Wynik reklamy10593873382626181482
Reklama mobilna17253994435944008978
AppLovin1063618907739174004
Applovin (określanie stawek)1328079684332308356
Chartboost,2873236629771172317
Platforma Czekoladowa (ustalanie stawek)6432849193975106527
Wielokanałowa (MdotM)9372067028804390441
Zdarzenie niestandardowe18351550913290782395
DT Exchange*
* Przed 21 września 2022 roku sieć ta nosiła nazwę „Fyber Marketplace”.
2179455223494392917
Wahania (określanie stawek)8419777862490735710
Ulotne3376427960656545613
Fyber*
* To źródło reklam jest używane w raportach historycznych.
4839637394546996422
i-mobile5208827440166355534
Ulepsz cyfrową (określanie stawek)159382223051638006
Giełda indeksowa (określanie stawek)4100650709078789802
InMobi7681903010231960328
InMobi (określanie stawek)6325663098072678541
Źródło Iron6925240245545091930
Leadbolt2899150749497968595
LG U+AD18298738678491729107
Maio7505118203095108657
maio (określanie stawek)1343336733822567166
Media.net (określanie stawek)2127936450554446159
Zapośredniczone autoreklamy6060308706800320801
Meta Audience Network*
* Do 6 czerwca 2022 roku ta sieć nosiła nazwę „Facebook Audience Network”.
10568273599589928883
Meta Audience Network (określanie stawek)*
* Do 6 czerwca 2022 roku ta sieć nosiła nazwę „Facebook Audience Network (określanie stawek)”.
11198165126854996598
MobFox8079529624516381459
MoPub (wycofane)10872986198578383917
mójcel8450873672465271579
Nend9383070032774777750
ONE by AOL (Millennial Media)6101072188699264581
ONE by AOL (Nexage)3224789793037044399
OpenX (określanie stawek)4918705482605678398
Pangle (określanie stawek)3525379893916449117
PubMatic (określanie stawek)3841544486172445473
Kampania z rezerwacjami7068401028668408324
RhythmOne (określanie stawek)2831998725945605450
Rubicon (określanie stawek)3993193775968767067
Planeta SK734341340207269415
Udział w aukcjach (określanie stawek)5247944089976324188
Smaato (określanie stawek)3362360112145450544
Równanie (określanie stawek)*

* Przed 12 stycznia 2023 roku ta sieć nazywała się „Smart Adserver”.

5970199210771591442
Sonobi (określanie stawek)3270984106996027150
Tapjoy7295217276740746030
Tapjoy (określanie stawek)4692500501762622178
Tencent GDT7007906637038700218
TripleLift (określanie stawek)8332676245392738510
Reklamy Unity4970775877303683148
UnrulyX (określanie stawek)2831998725945605450
Verizon Media7360851262951344112
Vpon1940957084538325905
Wzrost przychodów*

* Przed 30 stycznia 2023 roku ta sieć nazywała się „Vungle”.

1953547073528090325
Wzrost przychodów (określanie stawek)*

* Przed 30 stycznia 2023 roku ta sieć nosiła nazwę „Vungle (określanie stawek)”.

4692500501762622185
Yieldmo (określanie stawek)4193081836471107579
YieldOne (określanie stawek)3154533971590234104
Zucky5506531810221735863

Nagradzanie użytkowników

Ważne jest zachowanie równowagi między wrażeniami użytkownika a weryfikacją nagród podczas podejmowania decyzji o nagrodzie dla użytkownika. Mogą wystąpić opóźnienia w wywołaniach po stronie serwera przed uzyskaniem dostępu do systemów zewnętrznych. Z tego powodu zalecaną metodą jest używanie wywołania zwrotnego po stronie klienta w celu natychmiastowego nagradzania użytkownika, a jednocześnie sprawdzanie wszystkich nagród po otrzymaniu wywołań zwrotnych po stronie serwera. Takie podejście zapewnia użytkownikom dobre wrażenia, a jednocześnie zapewnia korzyści przyznawane nagrody.

W przypadku aplikacji, w których ważność nagrody ma kluczowe znaczenie (np. nagroda wpływa na gospodarkę gry w grze), a opóźnienia w realizacji nagród są dopuszczalne, czas oczekiwania na weryfikację po stronie serwera może być najlepszym podejściem.

Dane niestandardowe

Aplikacje, które wymagają dodatkowych danych w wywołaniach zwrotnych po stronie serwera, powinny używać funkcji danych niestandardowych reklam z nagrodą. Każda wartość ciągu ustawiona w obiekcie reklamy z nagrodą jest przekazywana do parametru zapytania custom_data wywołania zwrotnego SSV. Jeśli nie ustawisz żadnej niestandardowej wartości danych, w wywołaniu zwrotnym SSV nie będzie wartości parametru zapytania custom_data.

Poniższy kod pokazuje, jak ustawić opcje SSV po wczytaniu reklamy z nagrodą.

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

Jeśli chcesz ustawić niestandardowy ciąg nagród, musisz to zrobić przed wyświetleniem reklamy.

Ręczna weryfikacja SSV z nagrodą

Czynności wykonywane przez klasę RewardedAdsVerifier w celu zweryfikowania SSV z nagrodą można znaleźć poniżej. Uwzględnione fragmenty kodu są w języku Java i korzystają z biblioteki zewnętrznej Tink, ale możesz wykonać te czynności w wybranym przez siebie języku przy użyciu dowolnej biblioteki zewnętrznej, która obsługuje ECDSA.

Pobierz klucze publiczne

Aby zweryfikować wywołanie zwrotne SSV z nagrodą, potrzebujesz klucza publicznego dostarczonego przez AdMob.

Lista kluczy publicznych używanych do weryfikowania wywołań zwrotnych SSV z nagrodą można pobrać z serwera kluczy AdMob. Lista kluczy publicznych jest podana w formacie JSON w formacie podobnym do tego:

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

Aby pobrać klucze publiczne, połącz się z serwerem kluczy AdMob i pobierz klucze. Ten kod wykonuje to zadanie i zapisuje przedstawienie kluczy w zmiennej JSON w zmiennej 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();
}

Pamiętaj, że klucze publiczne są regularnie rotowane. Otrzymasz e-maila z informacją o nadchodzącej rotacji. Jeśli buforujesz klucze publiczne, zaktualizuj je po otrzymaniu tego e-maila.

Po pobraniu kluczy publicznych trzeba je przeanalizować. Poniższa metoda parsePublicKeysJson pobiera tekst w postaci ciągu JSON (jak w powyższym przykładzie) i tworzy mapowanie z wartości key_id do kluczy publicznych, które są zamknięte jako obiekty ECPublicKey z biblioteki 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;
}

Pobieranie treści do weryfikacji

Ostatnie 2 parametry zapytania z odwołaniami do reklam z nagrodą SSV mają zawsze wartość signature i key_id, w tej kolejności. Pozostałe parametry zapytania określają zawartość do zweryfikowania. Załóżmy, że masz skonfigurowane w AdMob wysyłanie wywołań zwrotnych do https://www.myserver.com/mypath. Fragment kodu poniżej przedstawia przykładowe wywołanie zwrotne reklamy SSV z wyróżnioną treścią.

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

Poniższy kod pokazuje, jak przeanalizować zawartość weryfikowaną z adresu URL wywołania zwrotnego jako tablicę bajtową 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"));

Pobierz podpis i key_id z adresu URL wywołania zwrotnego

Przeanalizuj parametry zapytania signature i key_id z adresu URL wywołania zwrotnego, używając wartości queryString z poprzedniego kroku, jak pokazano poniżej:

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

Przeprowadź weryfikację

Ostatnim krokiem jest sprawdzenie treści adresu URL wywołania zwrotnego za pomocą odpowiedniego klucza publicznego. Wykorzystaj mapowanie zwrócone w metodzie parsePublicKeysJson i użyj parametru key_id z adresu URL wywołania zwrotnego, aby uzyskać klucz publiczny z mapowania. Następnie zweryfikuj podpis przy użyciu tego klucza publicznego. Te kroki są pokazane poniżej w metodzie 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);
  }
}

Jeśli metoda jest wykonywana bez zgłaszania wyjątku, adres URL wywołania zwrotnego został zweryfikowany.

Najczęstsze pytania

Czy mogę zapisywać w pamięci podręcznej klucz publiczny udostępniony przez serwer kluczy AdMob?
Zalecamy zapisywanie w pamięci podręcznej klucza publicznego dostarczonego przez serwer AdMob w celu zmniejszenia liczby operacji wymaganych do weryfikacji wywołań zwrotnych SSV. Pamiętaj jednak, że klucze publiczne są regularnie rotowane i nie powinny być przechowywane w pamięci podręcznej dłużej niż 24 godziny.
Jak często są publikowane klucze publiczne dostarczane przez serwer kluczy AdMob?
Klucze publiczne dostarczane przez serwer kluczy AdMob są poddawane rotacji według harmonogramu. Aby weryfikacja wywołań zwrotnych usługi SSV nadal działała zgodnie z oczekiwaniami, klucze publiczne nie powinny być przechowywane w pamięci podręcznej dłużej niż 24 godziny.
Co się stanie, jeśli mój serwer nie będzie dostępny?
Google oczekuje HTTP 200 OK kodu stanu odpowiedzi w przypadku wywołań zwrotnych SSV. Jeśli serwer nie może nawiązać połączenia lub nie wyświetla oczekiwanej odpowiedzi, Google spróbuje ponownie wysłać wywołania SSV do pięciu razy w sekundach.
Jak sprawdzić, czy wywołania zwrotne SSV pochodzą od Google?
Aby sprawdzić, czy wywołania SSV pochodzą z Google, użyj odwrotnego wyszukiwania DNS.