Danos tu opinión sobre el SDK de anuncios de Google para móviles. Rellena la encuesta anual.

Validar retrollamadas de verificación en el servidor

Las retrollamadas de verificación del servidor (retrollamadas de SSV) son solicitudes de URL con parámetros de consulta mostrados por Google y que Google envía a un sistema externo para notificarle de que hay que premiar a un usuario por interactuar con un vídeo bonificado. En los anuncios bonificados, estas retrollamadas constituyen una capa más de protección contra la falsificación de retrollamadas de cliente para recompensar a los usuarios.

En esta guía aprenderás a verificar retrollamadas de SSV de vídeos bonificados mediante la biblioteca criptográfica de terceros Tink para comprobar la legitimidad de los parámetros de consulta de las retrollamadas. Si bien en esta guía utilizamos Tink, puedes emplear cualquier biblioteca de terceros compatible con ECDSA.

Requisitos previos

Utilizar RewardedAdsVerifier de Tink

El repositorio GitHub de Tink incluye la clase de ayuda RewardedAdsVerifier para reducir la cantidad de código que se necesita para verificar una retrollamada de SSV de un anuncio de vídeo bonificado. Si usas esta clase junto con la biblioteca criptográfica de terceros Tink, puedes ejecutar lo siguiente para verificar una URL de retrollamada:

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

Si la URL de retrollamada se verifica correctamente, el método verify() se ejecuta sin generar ninguna excepción. En la sección Recompensar a los usuarios puedes consultar las prácticas recomendadas con respecto a cuándo se debe recompensar a los usuarios. Si prefieres ver un desglose de los pasos que sigue esta clase para verificar las retrollamadas de SSV de vídeos bonificados, consulta la sección Verificación manual de SSV de vídeos bonificados.

Parámetros de la retrollamada de SSV

Las retrollamadas de SSV contienen parámetros de consulta que describen la interacción de los anuncios de vídeo bonificados. A continuación se indican los nombres y las descripciones de los parámetros, así como ejemplos de los valores que utilizan. Todos los parámetros se envían en orden alfabético.

Nombre del parámetro Descripción Valor de ejemplo
ad_network Identificador de la red publicitaria que ha mostrado el anuncio. Los nombres de red correspondientes a los valores de ID se enumeran en la sección Identificadores de redes publicitarias. 1953547073528090325
ad_unit ID del bloque de anuncios de AdMob utilizado para solicitar el anuncio de vídeo bonificado. 2747237135
custom_data Cadena de datos personalizados que proporciona setCustomData.

Si la aplicación no proporciona ninguna cadena de datos personalizados, este parámetro de consulta no estará presente en la retrollamada de SSV.

EJEMPLO_CADENA_DATOS_PERSONALIZADOS
key_id Clave que se utilizará para verificar la retrollamada de SSV. Este valor se asigna a una clave pública que facilita el servidor de claves de AdMob. 1234567890
reward_amount Importe de la bonificación especificada en la configuración del bloque de anuncios. 5
reward_item Elemento de bonificación, tal como se especifica en la configuración del bloque de anuncios. monedas
signature Firma de la retrollamada de SSV generada por AdMob. MEUCIQCLJS_s4ia_sN06HqzeW7Wc3nhZi4RlW3qV0oO-6AIYdQIgGJEh-rzKreO-paNDbSCzWGMtmgJHYYW9k2_icM9LFMY
timestamp Marca de tiempo Epoch, en milisegundos, que indica cuándo se recompensó al usuario. 1507770365237823
transaction_id Identificador único hexadecimal para cada evento de concesión de bonificación que genera AdMob. 18fa792de1bca816048293fc71035638
user_id Identificador de usuario que proporciona setUserId.

Si la aplicación no proporciona ningún identificador de usuario, este parámetro de consulta no estará presente en la retrollamada de SSV

1234567

Identificadores de redes publicitarias

En la siguiente tabla se muestra el nombre de la red publicitaria que corresponde a cada identificador de red publicitaria en las retrollamadas de SSV.

Nombre de red publicitaria ID de red publicitaria
AdColony 15586990674969969776
AdMob 5450213213286189855
AppLovin 1063618907739174004
Chartboost 2873236629771172317
Audience Network de Facebook 10568273599589928883
Fuse 8914788932458531264
Fyber 4839637394546996422
InMobi 7681903010231960328
maio 7505118203095108657
myTarget 8450873672465271579
Nend 9383070032774777750
TapJoy 7295217276740746030
Unity Ads 4970775877303683148
Vungle 1953547073528090325

Recompensar a los usuarios

A la hora de decidir cuándo se recompensa a un usuario, es importante equilibrar la experiencia de usuario con el proceso de validación de las bonificaciones. Las retrollamadas del servidor pueden tardar en llegar a los sistemas externos; por tanto, lo más recomendable es usar la retrollamada del cliente para recompensar al usuario de inmediato y validar todas las bonificaciones tras recibir una retrollamada del servidor. Esta estrategia mejora la experiencia de los usuarios, al tiempo que se asegura la validez de las bonificaciones concedidas.

Sin embargo, si la validez de la bonificación es importante (por ejemplo, porque afecta a la economía de tu aplicación durante el juego) y es aceptable que se produzcan ciertos retrasos en la concesión de las bonificaciones, puede que la mejor estrategia sea esperar a la retrollamada de verificación del servidor.

Datos personalizados

Las aplicaciones que requieren datos adicionales en las retrollamadas de verificación del servidor deben usar la función de datos personalizados de los anuncios bonificados. Los valores de cadena asignados a un objeto de anuncio bonificado se reenvían dentro del parámetro de consulta custom_data de la retrollamada de SSV. Si no se asigna ningún valor de datos personalizados, el valor del parámetro de consulta custom_data no estará presente en la retrollamada de SSV.

En el siguiente fragmento de código se muestra cómo asignar datos personalizados a objetos de anuncios bonificados antes de solicitar un anuncio.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    MobileAds.initialize(this, new OnInitializationCompleteListener() {
        @Override
        public void onInitializationComplete(InitializationStatus initializationStatus) {}
    });

    // Use an activity context to get the rewarded video instance.
    mRewardedVideoAd = MobileAds.getRewardedVideoAdInstance(this);
    mRewardedVideoAd.setRewardedVideoAdListener(this);

    loadRewardedVideoAd();
}

private void loadRewardedVideoAd() {
    mRewardedVideoAd.setCustomData("SAMPLE_CUSTOM_DATA_STRING");
    mRewardedVideoAd.loadAd("ca-app-pub-3940256099942544/5224354917",
            new AdRequest.Builder().build());
}

Verificación manual de SSV de vídeos bonificados

A continuación se describen los pasos que sigue la clase RewardedAdsVerifier para llevar a cabo la verificación SSV de un vídeo bonificado. Aunque los fragmentos de código incluidos están en Java y aprovechan la biblioteca de terceros Tink, puedes implementar estos pasos en el lenguaje que prefieras y utilizar cualquier biblioteca de terceros compatible con ECDSA.

Obtener claves públicas

Para verificar una retrollamada de SSV de vídeo bonificado, necesitas una clave pública proporcionada por AdMob.

En el servidor de claves de AdMob hay una lista de las claves públicas que se usan para validar las retrollamadas de SSV de vídeos bonificados. Dicha lista se proporciona como una representación JSON, con un formato similar al siguiente:

{
 "keys": [
    {
      keyId: 1916455855,
      pem: "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUaWMKcBHWdhUE+DncSIHhFCLLEln\nUs0LB9oanZ4K/FNICIM8ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw==\n-----END PUBLIC KEY-----"
      base64: "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEUaWMKcBHWdhUE+DncSIHhFCLLElnUs0LB9oanZ4K/FNICIM8ltS4nzc9yjmhgVQOlmSS6unqvN9t8sqajRTPcw=="
    },
    {
      keyId: 3901585526,
      pem: "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtxg2BsK/fllIeADtLspezS6YfHFWXZ8tiJncm8LDBa/NxEC84akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw==\n-----END PUBLIC KEY-----"
      base64: "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtxg2BsK/fllIeADtLspezS6YfHFWXZ8tiJncm8LDBa/NxEC84akdWbWDCUrMMGIV27/3/e7UuKSEonjGvaDUsw=="
    },
  ],
}

Para obtener las claves públicas, conéctate al servidor de claves de AdMob y descárgalas. El siguiente código realiza esta tarea y guarda la representación JSON de las claves en 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();
}

Después de obtener las claves públicas, es necesario analizarlas. El método parsePublicKeysJson toma como entrada una cadena JSON, como en el ejemplo anterior, y asigna valores de ID de clave a claves públicas, que se encapsulan como objetos ECPublicKey de la biblioteca 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;
}

Obtener el contenido que hay que verificar

Los dos últimos parámetros de consulta de las retrollamadas de SSV de los vídeos bonificados son siempre signature y key_id,, en ese orden. El resto de parámetros sirven para especificar qué contenido hay que verificar. Supongamos que has configurado AdMob para enviar retrollamadas de bonificación a https://www.myserver.com/mypath. En el siguiente fragmento de código se muestra un ejemplo de retrollamada de SSV para un vídeo bonificado; el contenido que hay que verificar aparece resaltado.


https://www.myserver.com/path?ad_network=5450213213286189855&ad_unit=12345678&reward_amount=10&reward_item=coins&timestamp=1507770365237823&transaction_id=1234567890ABCDEF1234567890ABCDEF&user_id=1234567&signature=MEUCIQDGx44BZgQU6TU4iYEo1nyzh3NgDEvqNAUXlax-XPBQ5AIgCXSdjgKZvs_6QNYad29NJRqwGIhGb7GfuI914MDDZ1c&key_id=1268222887

En el siguiente código se muestra cómo analizar el contenido que hay que verificar desde una URL de retrollamada, como una matriz de bytes 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"));

Obtener la firma y el ID de clave de la URL de retrollamada

Usa el valor de queryString del paso anterior para analizar los parámetros de consulta signature y key_id de la URL de retrollamada, como se muestra a continuación:

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

Realizar la verificación

El último paso es verificar el contenido de la URL de retrollamada mediante la clave pública correspondiente. Toma la asignación que devuelva el método parsePublicKeysJson y utiliza el parámetro key_id de la URL de retrollamada para obtener la clave pública de dicha asignación. A continuación, verifica la firma con esa clave pública. Estos pasos se muestran a continuación en el método 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, "SHA256WithECDSA");
    verifier.verify(signature, dataToVerify);
  } else {
    throw new GeneralSecurityException("cannot find verifying key with key id: " + keyId);
  }
}

Si la URL de retrollamada se verifica correctamente, el método se ejecuta sin generar ninguna excepción.

Preguntas frecuentes

¿Puedo almacenar en caché la clave pública que proporciona el servidor de claves de AdMob?
Te recomendamos que lo hagas para reducir el número de operaciones necesarias para validar las retrollamadas de SSV. Sin embargo, ten en cuenta que las claves públicas rotan periódicamente y no deben almacenarse en caché durante más de 24 horas.
¿Con qué frecuencia rotan las claves públicas que proporciona el servidor de claves de AdMob?
Rotan con una programación variable. Para garantizar que la verificación de las retrollamadas de SSV sigue funcionando correctamente, las claves públicas no deben almacenarse en caché durante más de 24 horas.
¿Qué ocurre si no se puede establecer la conexión con mi servidor?
Google espera un código de respuesta de estado correcto HTTP 200 OK para las retrollamadas de SSV. Si no se puede establecer la conexión con tu servidor o este no proporciona la respuesta esperada, Google intentará enviar de nuevo las retrollamadas de SSV hasta cinco veces, con intervalos de un segundo.
¿Cómo puedo verificar que las retrollamadas de SSV proceden de Google?
Utiliza la petición de DNS invertida para verificar que provienen de Google.