Decriptazione degli identificatori degli inserzionisti per le reti pubblicitarie

Reti pubblicitarie che utilizzano I tag JavaScript per riempire gli annunci tramite Authorized Buyers sono idonei a ricevere identificatori dell'inserzionista per i dispositivi Android e iOS. Le informazioni vengono inviate tramite il %%EXTRA_TAG_DATA%% oppure Macro %%ADVERTISING_IDENTIFIER%% nel tag JavaScript gestito da Authorized Buyers. Il resto di questa sezione si concentra sull'estrazione %%EXTRA_TAG_DATA%% ma vedi Remarketing con IDFA o ID pubblicità per maggiori dettagli sul buffer di protocollo criptato %%ADVERTISING_IDENTIFIER%% MobileAdvertisingId che possono essere decriptati analogamente.

Cronologia

  1. La rete pubblicitaria aggiorna i propri tag in-app JavaScript tramite l'interfaccia utente di Authorized Buyers, aggiungendo la macro %%EXTRA_TAG_DATA%% come spiegato di seguito.
  2. Al momento della pubblicazione, l'app richiede un annuncio ad Authorized Buyers tramite la Google Mobile Ads SDK e trasmettere in modo sicuro l'identificatore dell'inserzionista.
  3. L'app riceve il tag JavaScript, con %%EXTRA_TAG_DATA%% compilata con il buffer di protocollo della rete pubblicitaria criptato contenente questo identificatore.
  4. L'app esegue questo tag, effettuando una chiamata alla rete pubblicitaria per annuncio.
  5. Per utilizzare (monetizzare) queste informazioni, la rete pubblicitaria deve elaborare il buffer di protocollo:
    1. Decodifica la stringa websafe di nuovo in una bytestring con WebSafeBase64.
    2. Decriptalo utilizzando lo schema descritto di seguito.
    3. Deserializza il protocollo e ottieni l'ID inserzionista da ExtraTagData.advertising_id o ExtraTagData.hashed_idfa.

Dipendenze

  1. Lo strumento WebSafeBase64 dell'architettura.
  2. Una libreria crittografica che supporta HMAC SHA-1, ad esempio Openssl.
  3. Il protocollo Google compilatore del buffer.

Decodifica la stringa websafe

Poiché le informazioni inviate tramite la macro %%EXTRA_TAG_DATA%% Deve essere inviato tramite URL, i server di Google lo codificano con la tecnologia sicura per il web Base64 (RFC 3548).

Prima di tentare pertanto, è necessario decodificare i caratteri ASCII in un bytestring. Il codice C++ di esempio riportato di seguito si basa sul protocollo OpenSSL BIO_f_base64() del progetto e fa parte dell'esempio di Google il codice di decrittografia.

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

Struttura della stringa di byte criptata

Dopo aver decodificato i caratteri ASCII in una bytestring, sei pronto per decriptarlo. La bytestringa criptata contiene tre sezioni:

  • initialization_vector: 16 byte.
  • ciphertext: serie di sezioni da 20 byte.
  • integrity_signature: 4 byte.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}

L'array di ciphertext byte è diviso in multipli da 20 byte specifiche, ad eccezione del fatto che l'ultima sezione può contenere tra 1 e 20 byte (inclusi). Per ogni sezione dell'originale byte_array, il valore corrispondente per ciphertext da 20 byte viene generato come segue:

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

dove || è una concatenazione.

Definizioni

Variabile Dettagli
initialization_vector 16 byte: univoci per l'impressione.
encryption_key 32 byte - forniti al momento della creazione dell'account.
integrity_key 32 byte - forniti al momento della creazione dell'account.
byte_array Un oggetto ExtraTagData serializzato, in sezioni da 20 byte.
counter_bytes Il valore del byte che mostra il numero ordinale della sezione, vedi di seguito.
final_message Matrice di byte totali inviati tramite la macro %%EXTRA_TAG_DATA%% (meno la codifica WebSafeBase64).
Operatori Dettagli
hmac(key, data) HMAC SHA-1, utilizzando key per criptare data.
a || b stringa a concatenata con la stringa b.

Calcolare contatori_byte

counter_bytes contrassegna l'ordine di ogni sezione da 20 byte della ciphertext. Tieni presente che l'ultima sezione può contenere tra 1 e 1 20 byte inclusi. Per inserire il valore corretto in counter_bytes quando esegui la funzione hmac(), conta le sezioni da 20 byte (incluso il resto) e utilizza la seguente tabella di riferimento:

Numero sezione Valore counter_bytes
0 Nessuno
1...256 1 byte. Il valore viene incrementato da 0 a 255 in sequenza.
257...512 2 byte. Il valore del primo byte è 0, il valore del secondo byte incrementi da 0 a 255 in sequenza.
513...768 3 byte. Il valore dei primi due byte è pari a 0, il valore dell'ultimo byte incrementi da 0 a 255 in sequenza.

Torna all'inizio

Schema di crittografia

Lo schema di crittografia si basa sullo stesso schema utilizzato per decriptare il indicatore di targeting iperlocale.

  1. Serializzazione: un'istanza dell'oggetto ExtraTagData come definita nel buffer di protocollo viene prima serializzato SerializeAsString() a un array di byte.

  2. Crittografia: l'array di byte viene quindi criptato tramite una schema di crittografia personalizzato progettato per ridurre al minimo l'overhead delle dimensioni garantendo al contempo una sicurezza adeguata. Lo schema di crittografia utilizza un algoritmo HMAC con chiave per generare un secret pad basato su initialization_vector, che è l'esclusivo l'evento di impressione.

Pseudocodice di crittografia

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

Schema di decriptazione

Il codice di decriptazione deve 1) decriptare il buffer di protocollo utilizzando e 2) verificare i bit di integrità con la chiave di integrità. Le chiavi saranno forniti durante la creazione dell'account. Non sono previste limitazioni a strutturare la tua implementazione. Per la maggior parte, dovresti essere in grado di codice campione e adattarlo alle tue esigenze.

  1. Genera il tuo pad: HMAC(encryption_key, initialization_vector || counter_bytes)
  2. XOR: prendi questo risultato e <xor> con testo crittografato per invertire la crittografia.
  3. Verifica: la firma dell'integrità trasmette 4 byte di HMAC(integrity_key, byte_array || initialization_vector)

Pseudocodice di decrittografia

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

Codice C++ di esempio

Qui c'è una funzione chiave del nostro decrittografia esempio di codice.

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

Ottieni dati dal buffer di protocollo della rete pubblicitaria

Una volta decodificati e decriptati i dati trasmessi %%EXTRA_TAG_DATA%%, puoi deserializzare il buffer di protocollo e ottieni l'identificatore dell'inserzionista per il targeting.

Se non hai dimestichezza con i buffer di protocollo, consulta la nostra documentazione.

Definizione

Il nostro buffer di protocollo della rete pubblicitaria è definito come segue:

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

Dovrai deserializzarlo utilizzando ParseFromString() come descritto in Documentazione relativa al buffer di protocollo C++.

Per informazioni dettagliate sull'advertising_id per Android e iOS hashed_idfa campi; consulta Decriptare ID pubblicità e targeting per app mobile inventario con IDFA.

Libreria Java

Anziché implementare gli algoritmi di crittografia per codificare e decodificare gli identificatori degli inserzionisti per le reti pubblicitarie, puoi usare DoubleClickCrypto.java. Per ulteriori informazioni, vedi Crittografia.