Déchiffrement des identifiants d'annonceur pour les réseaux publicitaires

Réseaux publicitaires utilisant Les tags JavaScript permettant de remplir les annonces via Authorized Buyers sont éligibles pour : reçoivent les identifiants des annonceurs pour les appareils Android et iOS. Les informations sont envoyées via le %%EXTRA_TAG_DATA%% ou %%ADVERTISING_IDENTIFIER%% de la balise JavaScript gérée par Authorized Buyers. Le reste de cette section est consacré à l'extraction %%EXTRA_TAG_DATA%%, mais voir <ph type="x-smartling-placeholder"></ph> Remarketing avec IDFA ou identifiant publicitaire pour en savoir plus sur le tampon proto chiffré %%ADVERTISING_IDENTIFIER%% MobileAdvertisingId qui peut être déchiffrée de la même manière.

Chronologie

  1. Le réseau publicitaire met à jour ses tags JavaScript dans les applications via l'interface utilisateur Authorized Buyers, en ajoutant la macro %%EXTRA_TAG_DATA%%, comme expliqué ci-dessous.
  2. Au moment de la diffusion, l'application demande une annonce à Authorized Buyers via la méthode SDK Google Mobile Ads, tandis que la transmission sécurisée de l'identifiant de l'annonceur.
  3. L'application reçoit en retour la balise JavaScript, avec le %%EXTRA_TAG_DATA%%. renseignée avec le tampon du protocole chiffré du réseau publicitaire contenant cet identifiant.
  4. L'application utilise ce tag et appelle le réseau publicitaire annonce.
  5. Pour utiliser (monétiser) ces informations, le réseau publicitaire doit traiter Protocol Buffer: <ph type="x-smartling-placeholder">
      </ph>
    1. Décodez la chaîne Web sécurisée en une chaîne d'octets avec WebSafeBase64.
    2. Déchiffrez-le à l'aide du schéma décrit ci-dessous.
    3. Désérialiser le proto et obtenir la référence annonceur de ExtraTagData.advertising_id ou ExtraTagData.hashed_idfa.

Dépendances

  1. La base de données WebSafeBase64 encodeur.
  2. Bibliothèque de chiffrement compatible avec SHA-1 HMAC, telle que Openssl
  3. Le protocole Google le compilateur de tampon.

Décoder une chaîne Web sécurisée

Comme les informations envoyées via la macro %%EXTRA_TAG_DATA%% doit être envoyé via une URL, les serveurs de Google l'encodent en base64 adaptée au Web (RFC 3548).

Avant d'essayer vous devez donc décoder les caractères ASCII dans bytestring. L'exemple de code C++ ci-dessous est basé sur la bibliothèque OpenSSL BIO_f_base64() du projet, et fait partie de l'exemple de code Google code de déchiffrement.

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<char*>(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);
}

Structure de la chaîne d'octets chiffrée

Une fois que vous avez décodé les caractères ASCII dans une chaîne d'octets, vous êtes prêt pour la déchiffrer. La chaîne d'octets chiffrée contient trois sections:

  • initialization_vector: 16 octets.
  • ciphertext: série de sections de 20 octets.
  • integrity_signature: 4 octets.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}

Le tableau d'octets ciphertext est divisé en plusieurs blocs de 20 octets à l'exception que la toute dernière section peut contenir entre 1 et 20 octets inclus. Pour chaque section de l'original, byte_array, la valeur ciphertext correspondante de 20 octets est généré comme suit:

<byte_array <xor> HMAC(encryption_key, initialization_vector || counter_bytes)>

|| est une concaténation.

Définitions

Variable Détails
initialization_vector 16 octets : unique pour l'impression.
encryption_key 32 octets, fournis lors de la configuration du compte.
integrity_key 32 octets, fournis lors de la configuration du compte.
byte_array Objet ExtraTagData sérialisé, en sections de 20 octets.
counter_bytes Valeur d'octet indiquant le numéro ordinal de la section (voir ci-dessous).
final_message Tableau d'octets total envoyé via la macro %%EXTRA_TAG_DATA%% (moins l'encodage WebSafeBase64).
Opérateurs Détails
hmac(key, data) HMAC SHA-1, utilisant key pour chiffrer data.
a || b Chaîne a concaténée avec la chaîne b.

Calculer compteur_bytes

counter_bytes marque l'ordre de chaque section de 20 octets du ciphertext Notez que la dernière section peut contenir entre 1 et 20 octets inclus. Pour remplir counter_bytes avec la valeur correcte lorsque vous exécutez votre fonction hmac(), comptez les sections de 20 octets (y compris le reste) et utilisez le tableau de référence suivant:

Numéro de section Valeur counter_bytes
0 Aucun
1 ... 256 1 octet. La valeur passe de 0 à 255 dans l'ordre indiqué.
257 ... 512 2 octets. La valeur du premier octet est 0, la valeur du deuxième octet de 0 à 255 successivement.
513 ... 768 3 octets. La valeur des deux premiers octets est 0, la valeur du dernier octet. de 0 à 255 successivement.

Haut de page

Schéma de chiffrement

Le schéma de chiffrement est basé sur le même schéma que celui utilisé pour déchiffrer le le signal de ciblage hyperlocalisé.

  1. Sérialisation: instance de l'objet ExtraTagData en tant que défini dans le tampon de protocole est d'abord sérialisé via SerializeAsString() en un tableau d'octets.

  2. Chiffrement: le tableau d'octets est ensuite chiffré à l'aide d'un de chiffrement personnalisé, conçu pour minimiser l'impact sur la taille tout en garantissant une sécurité adéquate. Le schéma de chiffrement utilise un algorithme HMAC à clé pour générer un bloc secret basé sur initialization_vector, qui est propre à l'événement d'impression.

Pseudo-code de chiffrement

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

Schéma de déchiffrement

Votre code de déchiffrement doit 1) déchiffrer le Protocol Buffer à l'aide du chiffrement clé et 2) vérifier les bits d’intégrité avec la clé d’intégrité. Les clés seront fournies lors de la configuration du compte. Aucune restriction ne s'applique structurer votre implémentation. Dans la plupart des cas, vous devriez être en mesure de prendre un exemple de code et l'adapter en fonction de vos besoins.

  1. Générer le pad: HMAC(encryption_key, initialization_vector || counter_bytes)
  2. XOR: prenez ce résultat et <xor> avec le le texte chiffré pour inverser le chiffrement.
  3. Verify (Vérifier) : la signature d'intégrité transmet 4 octets de HMAC(integrity_key, byte_array || initialization_vector)

Pseudo-code de déchiffrement

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

Exemple de code C++

Voici une fonction clé de notre documentation complète déchiffrement exemple de code.

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

Obtenir des données à partir du tampon de protocole du réseau publicitaire

Une fois que vous avez décodé et déchiffré les données transmises %%EXTRA_TAG_DATA%%, vous êtes prêt à désérialiser le tampon de protocole et d'obtenir l'identifiant de l'annonceur à des fins de ciblage.

Si vous ne connaissez pas les tampons de protocole, commencez par consulter notre documentation.

Définition

Notre tampon de protocole de réseau publicitaire est défini comme suit:

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

Vous devez la désérialiser à l'aide de ParseFromString(), comme décrit dans Documentation sur les tampons de protocole C++

Pour en savoir plus sur Android advertising_id et iOS hashed_idfa, consultez la section Déchiffrer Identifiant publicitaire et ciblage d'applications mobiles avec l'IDFA.

bibliothèque Java

Au lieu d’implémenter les algorithmes de chiffrement pour encoder et décoder les identifiants de l'annonceur pour les réseaux publicitaires, <ph type="x-smartling-placeholder"></ph> DoubleClickCrypto.java. Pour en savoir plus, consultez Cryptographie :