Las redes de publicidad que usan
etiquetas de JavaScript para completar anuncios mediante Authorized Buyers son aptas para recibir identificadores de anunciantes en dispositivos iOS y Android.
La información se envía a través de las macros %%EXTRA_TAG_DATA%%
o %%ADVERTISING_IDENTIFIER%%
en la etiqueta de JavaScript administrada por Authorized Buyers. El resto de esta sección se enfoca en la extracción de %%EXTRA_TAG_DATA%%
, pero consulta
Remarketing con IDFA o ID de publicidad para obtener detalles sobre el búfer de protocolo encriptado de %%ADVERTISING_IDENTIFIER%%
MobileAdvertisingId
que se puede desencriptar de forma análoga.
Cronograma
- La red de publicidad actualiza sus etiquetas de JavaScript en la aplicación a través de la IU de Authorized Buyers y agrega la macro
%%EXTRA_TAG_DATA%%
como se explica a continuación. - Durante la publicación, la app solicita un anuncio a Authorized Buyers mediante el SDK de anuncios de Google para dispositivos móviles y pasa el identificador de anunciante de forma segura.
- La app vuelve a recibir la etiqueta de JavaScript, con la macro
%%EXTRA_TAG_DATA%%
completada con el búfer de protocolo de la red de publicidad encriptado que contiene ese identificador. - La app ejecuta esta etiqueta y realiza una llamada a la red de publicidad para el anuncio ganador.
- Para utilizar (monetizar) esta información, la red de publicidad debe procesar el búfer de protocolo:
- Vuelve a decodificar la string websafe en una cadena de bytes con WebSafeBase64.
- Desencriptarlo con el esquema que se describe a continuación.
- Deserializa el proto y obtén el ID del anunciante de ExtraTagData.advertising_id o ExtraTagData.hashed_idfa.
Dependencias
- El codificador WebSafeBase64
- Una biblioteca criptográfica compatible con SHA-1 HMAC, como Openssl.
- El compilador de búfer de protocolo de Google.
Decodificar string segura para la Web
Debido a que la información enviada con la macro %%EXTRA_TAG_DATA%%
debe enviarse a través de URL, los servidores de Google la codifican con base64 segura para la Web (RFC 3548).
Por lo tanto, antes de intentar desencriptarlos, debes decodificar los caracteres ASCII nuevamente en una cadena de bytes. El código de muestra de C++ que aparece a continuación se basa en el BIO_f_base64() del proyecto OpenSSL y forma parte del código de desencriptación de muestra de Google.
string AddPadding(const string& b64_string) { if (b64_string.size() % 4 == 3) { return b64_string + "="; } else if (b64_string.size() % 4 == 2) { return b64_string + "=="; } return b64_string; } // Adapted from http://www.openssl.org/docs/man1.1.0/crypto/BIO_f_base64.html // Takes a web safe base64 encoded string (RFC 3548) and decodes it. // Normally, web safe base64 strings have padding '=' replaced with '.', // but we will not pad the ciphertext. We add padding here because // openssl has trouble with unpadded strings. string B64Decode(const string& encoded) { string padded = AddPadding(encoded); // convert from web safe -> normal base64. int32 index = -1; while ((index = padded.find_first_of('-', index + 1)) != string::npos) { padded[index] = '+'; } index = -1; while ((index = padded.find_first_of('_', index + 1)) != string::npos) { padded[index] = '/'; } // base64 decode using openssl library. const int32 kOutputBufferSize = 256; char output[kOutputBufferSize]; BIO* b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO* bio = BIO_new_mem_buf(const_cast(padded.data()), padded.length()); bio = BIO_push(b64, bio); int32 out_length = BIO_read(bio, output, kOutputBufferSize); BIO_free_all(bio); return string(output, out_length); }
Estructura de la cadena de bytes encriptada
Una vez que hayas decodificado los caracteres ASCII en una cadena de bytes, estarás listo para desencriptarlos. La cadena de bytes encriptada contiene 3 secciones:
initialization_vector
: 16 bytes.ciphertext
: Serie de secciones de 20 bytes.integrity_signature
: 4 bytes.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}
El array de bytes ciphertext
se divide en varias secciones de 20 bytes, con la excepción de que la última sección puede contener entre 1 y 20 bytes inclusive. Para cada sección del byte_array
original, se genera el ciphertext
de 20 bytes correspondiente de la siguiente manera:
<byte_array <xor> HMAC(encryption_key, initialization_vector || counter_bytes)>
En el ejemplo anterior, ||
es concatenación.
Definiciones
Variable | Detalles |
---|---|
initialization_vector |
16 bytes: único para la impresión. |
encryption_key |
32 bytes, que se proporcionan durante la configuración de la cuenta. |
integrity_key |
32 bytes, que se proporcionan durante la configuración de la cuenta. |
byte_array |
Un objeto ExtraTagData serializado, en secciones de 20 bytes. |
counter_bytes |
Valor de bytes que muestra el número ordinal de la sección; consulta a continuación. |
final_message |
Array de bytes total enviado a través de la macro %%EXTRA_TAG_DATA%% (menos la codificación WebSafeBase64). |
Operadores | Detalles |
---|---|
hmac(key, data) |
SHA-1 HMAC que usa key para encriptar data . |
a || b |
Se concatenó la cadena a con la cadena b . |
Calcular contador_bytes
counter_bytes
marca el orden de cada sección de 20 bytes de ciphertext
. Ten en cuenta que la última sección puede contener entre 1 y 20 bytes inclusive. Para completar counter_bytes
con el valor correcto cuando ejecutas la función hmac()
, cuenta las secciones de 20 bytes (incluido el resto) y usa la siguiente tabla de referencia:
Número de sección | Valor counter_bytes |
---|---|
0 | Ninguna |
1 ... 256 | 1 byte. El valor aumenta de 0 a 255 de forma secuencial. |
257 ... 512 | 2 bytes. El valor del primer byte es 0; el valor del segundo byte incrementa de 0 a 255 de forma secuencial. |
513 ... 768 | 3 bytes. El valor de los dos primeros bytes es 0; el valor del último byte aumenta de 0 a 255 de forma secuencial. |
Esquema de encriptación
El esquema de encriptación se basa en el mismo esquema que se usa para desencriptar el indicador de segmentación hiperlocal.
Serialización: Una instancia del objeto ExtraTagData como se define en el búfer de protocolo primero se serializa a través de
SerializeAsString()
en un array de bytes.Encriptación: El array de bytes se encripta con un esquema de encriptación personalizado diseñado para minimizar la sobrecarga de tamaño y, al mismo tiempo, garantizar una seguridad adecuada. El esquema de encriptación usa un algoritmo HMAC con clave para generar un bloque secreto basado en el
initialization_vector
, que es único para el evento de impresión.
Pseudocódigo de encriptación
byte_array = SerializeAsString(ExtraTagData object) pad = hmac(encryption_key, initialization_vector || counter_bytes ) // for each 20-byte section of byte_array ciphertext = pad <xor> byte_array // for each 20-byte section of byte_array integrity_signature = hmac(integrity_key, byte_array || initialization_vector) // first 4 bytes final_message = initialization_vector || ciphertext || integrity_signature
Esquema de desencriptación
Tu código de desencriptación debe 1) desencriptar el búfer de protocolo con la clave de encriptación y 2) verificar los bits de integridad con la clave de integridad. Las claves se te proporcionarán durante la configuración de la cuenta. No hay restricciones sobre cómo estructurar tu implementación. En general, debes poder tomar el código de muestra y adaptarlo según tus necesidades.
- Genera tu pad:
HMAC(encryption_key, initialization_vector || counter_bytes)
- XOR: Toma este resultado y
<xor>
con el texto cifrado para revertir la encriptación. - Verificar: La firma de integridad pasa 4 bytes de
HMAC(integrity_key, byte_array || initialization_vector)
.
Pseudocódigo de desencriptación
// split up according to length rules (initialization_vector, ciphertext, integrity_signature) = final_message // for each 20-byte section of ciphertext pad = hmac(encryption_key, initialization_vector || counter_bytes) // for each 20-byte section of ciphertext byte_array = ciphertext <xor> pad confirmation_signature = hmac(integrity_key, byte_array || initialization_vector) success = (confirmation_signature == integrity_signature)
Ejemplo de código C++
Aquí se incluye una función clave de nuestro código de ejemplo de desencriptación completo.
bool DecryptByteArray( const string& ciphertext, const string& encryption_key, const string& integrity_key, string* cleartext) { // Step 1. find the length of initialization vector and clear text. const int cleartext_length = ciphertext.size() - kInitializationVectorSize - kSignatureSize; if (cleartext_length < 0) { // The length cannot be correct. return false; } string iv(ciphertext, 0, kInitializationVectorSize); // Step 2. recover clear text cleartext->resize(cleartext_length, '\0'); const char* ciphertext_begin = string_as_array(ciphertext) + iv.size(); const char* const ciphertext_end = ciphertext_begin + cleartext->size(); string::iterator cleartext_begin = cleartext->begin(); bool add_iv_counter_byte = true; while (ciphertext_begin < ciphertext_end) { uint32 pad_size = kHashOutputSize; uchar encryption_pad[kHashOutputSize]; if (!HMAC(EVP_sha1(), string_as_array(encryption_key), encryption_key.length(), (uchar*)string_as_array(iv), iv.size(), encryption_pad, &pad_size)) { printf("Error: encryption HMAC failed.\n"); return false; } for (int i = 0; i < kBlockSize && ciphertext_begin < ciphertext_end; ++i, ++cleartext_begin, ++ciphertext_begin) { *cleartext_begin = *ciphertext_begin ^ encryption_pad[i]; } if (!add_iv_counter_byte) { char& last_byte = *iv.rbegin(); ++last_byte; if (last_byte == '\0') { add_iv_counter_byte = true; } } if (add_iv_counter_byte) { add_iv_counter_byte = false; iv.push_back('\0'); } }
Cómo obtener datos del búfer de protocolo de la red publicitaria
Una vez que hayas decodificado y desencriptado los datos que se pasaron en %%EXTRA_TAG_DATA%%
, estarás listo para deserializar el búfer de protocolo y obtener el identificador de anunciante para la segmentación.
Si no conoces los búferes de protocolo, consulta nuestra documentación.
Definición
Nuestro búfer de protocolo de la red de publicidad se define de la siguiente manera:
message ExtraTagData { // advertising_id can be Apple's identifier for advertising (IDFA) // or Android's advertising identifier. When the advertising_id is an IDFA, // it is the plaintext returned by iOS's [ASIdentifierManager // advertisingIdentifier]. For hashed_idfa, the plaintext is the MD5 hash of // the IDFA. Only one of the two fields will be available, depending on the // version of the SDK making the request. Later SDKs provide unhashed values. optional bytes advertising_id = 1; optional bytes hashed_idfa = 2; }
Deberás deserializarla con ParseFromString()
como se describe en la documentación del búfer de protocolo C++.
Para obtener detalles sobre los campos hashed_idfa
de iOS y advertising_id
de Android, consulta Desencriptar el ID de publicidad y Cómo segmentar el inventario de apps para dispositivos móviles con el IDFA.
Biblioteca Java
En lugar de implementar los algoritmos criptográficos para codificar y decodificar los identificadores de anunciantes para redes de publicidad, puedes usar DoubleClickCrypto.java. Para obtener más información, consulta Criptografía.