Decriptazione dei prezzi di conferma

Quando la tua creatività vince un'asta, Google può informarti del prezzo vincente se la creatività include la macro ${AUCTION_PRICE}.

Quando la macro viene espansa, restituisce il prezzo vincente in forma criptata. Può essere incluso in una creatività, ad esempio con una richiesta di pixel invisibile visualizzata nell'ambito dell'annuncio:

<div>
  <script language='JavaScript1.1' src='https://example.com?creativeID=5837243'/>
  <img src='https://example.com/t.gif?price=${AUCTION_PRICE}' width='1' height='1'/>
</div>

La macro ${AUCTION_PRICE} può essere inclusa anche nell'URL VAST di una creatività video, ma non nell'URL impressione in VAST:

https://example.com/vast/v?price=${AUCTION_PRICE}

Scenario

  1. L'applicazione per le offerte OpenRTB include la macro ${AUCTION_PRICE} nello snippet HTML o nell'URL VAST restituito a Google.
  2. Google sostituisce il prezzo vincente della macro con una codifica Base64 sicura per il web non riempita (RFC 3548).
  3. Lo snippet passa la conferma nel formato scelto. Ad esempio, la conferma potrebbe essere passata nell'URL di una richiesta di pixel invisibile visualizzata nell'ambito dell'annuncio.
  4. Sul server, la tua applicazione decodifica le informazioni sul prezzo vincente e decripta il risultato in base64 web-safe.

Dipendenze

Avrai bisogno di una libreria di crittografia che supporti SHA-1 HMAC, ad esempio Openssl.

Codice di esempio

Il codice di esempio è fornito in Java e C++ e può essere scaricato dal progetto privatedatacommunicationprotocol.

  • Il codice di esempio Java utilizza il decodificatore base64 del progetto Apache Commons. Non è necessario scaricare il codice Apache Commons, poiché l'implementazione di riferimento include la parte necessaria ed è quindi autonoma.

  • Il codice di esempio C++ utilizza il metodo BIO base64 di OpenSSL. Prende una stringa sicura per il web codificata in base64 (RFC 3548) e la decodifica. In genere, le stringhe base64 sicure per il web sostituiscono il riempimento "=" con "." (tieni presente che le virgolette vengono aggiunte per una lettura più chiara e non sono incluse nel protocollo), ma la sostituzione della macro non riempie il prezzo criptato. L'implementazione di riferimento aggiunge il padding perché OpenSSL ha problemi con le stringhe non con padding.

Codifica

La crittografia e la decrittografia del prezzo vincente richiedono due chiavi segrete, ma condivise. Una chiave di integrità e una chiave di crittografia, denominate rispettivamente i_key e e_key. Entrambe le chiavi vengono fornite durante la configurazione dell'account come stringhe base64 sicure per il web e sono disponibili nella pagina Authorized Buyers in Impostazioni stimolante > Impostazioni RTB > Chiavi di crittografia.

Esempi di chiavi di integrità e crittografia:

skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=  // Encryption key (e_key)
arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=  // Integrity key (i_key)

Le chiavi devono essere decodificate in modo sicuro per il web e poi decodificate in base64 dall'applicazione:

e_key = WebSafeBase64Decode('skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=')
i_key = WebSafeBase64Decode('arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=')

Schema di crittografia

Il prezzo viene criptato utilizzando uno schema di crittografia personalizzato progettato per minimizzare il sovraccarico delle dimensioni garantendo al contempo una sicurezza adeguata. Lo schema di crittografia utilizza un algoritmo HMAC con chiave per generare un padding segreto in base all'ID evento impressione unico.

Il prezzo criptato ha una lunghezza fissa di 28 byte. È costituito da un vettore di inizializzazione di 16 byte, 8 byte di testo crittografato e una firma di integrità di 4 byte. Il prezzo criptato è codificato in base64 sicuro per il web, in base a RFC 3548, con i caratteri di spaziatura interna omessi. Pertanto, il prezzo criptato di 28 byte viene codificato come stringa base64 sicura per il web di 38 caratteri, indipendentemente dal prezzo vincente pagato.

Esempi di prezzi criptati:

YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCce_6msaw  // 100 CPI micros
YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCAWJRxOgA  // 1900 CPI micros
YWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw  // 2700 CPI micros

Il formato criptato è:

{initialization_vector (16 bytes)}{encrypted_price (8 bytes)}
{integrity (4 bytes)}

Il prezzo è criptato come <price xor HMAC(encryption_key, initialization_vector)>, quindi la decrittografia calcolaHMAC(encryption_key,initialization_vector) ed esegue XOR con il prezzo criptato per annullare la crittografia. La fase di integrità richiede 4 byte di <HMAC(integrity_key, price||initialization_vector)> dove || è la concatenazione.

Input
iv vettore di inizializzazione (16 byte, univoco per l'impressione)
e_key Chiave di crittografia (32 byte, fornita durante la configurazione dell'account)
i_key Chiave di integrità (32 byte, fornita durante la configurazione dell'account)
price (8 byte - in micro della valuta dell'account)
Notazione
hmac(k, d) HMAC SHA-1 dei dati d, utilizzando la chiave k
a || b stringa a concatenata con stringa b
Pseudocodice
pad = hmac(e_key, iv)  // first 8 bytes
enc_price = pad <xor> price
signature = hmac(i_key, price || iv)  // first 4 bytes

final_message = WebSafeBase64Encode( iv || enc_price || signature )

Schema di decriptazione

Il codice di decrittografia deve decriptare il prezzo utilizzando la chiave di crittografia e verificare i bit di integrità con la chiave di integrità. Le chiavi ti verranno fornite durante la configurazione. Non ci sono limitazioni relative ai dettagli della struttura dell'implementazione. Nella maggior parte dei casi, dovresti essere in grado di prendere il codice di esempio e adattarlo alle tue esigenze.

Input
e_key chiave di crittografia, 32 byte, fornita durante la configurazione dell'account
i_key Chiave di integrità, 32 byte: fornita durante la configurazione dell'account
final_message 38 caratteri con codifica Base64 sicura per il web
Pseudocodice
// Base64 padding characters are omitted.
// Add any required base64 padding (= or ==).
final_message_valid_base64 = AddBase64Padding(final_message)

// Web-safe decode, then base64 decode.
enc_price = WebSafeBase64Decode(final_message_valid_base64)

// Message is decoded but remains encrypted.
(iv, p, sig) = enc_price // Split up according to fixed lengths.
price_pad = hmac(e_key, iv)
price = p <xor> price_pad

conf_sig = hmac(i_key, price || iv)
success = (conf_sig == sig)

Rilevare attacchi con risposte non aggiornate

Per rilevare gli attacchi di replay o di risposta non aggiornata, ti consigliamo di filtrare le risposte con un timestamp molto diverso dall'ora del sistema, dopo aver tenuto conto delle differenze di fuso orario.

Il vettore di inizializzazione contiene un timestamp nei primi 8 byte. Può essere letto dalla seguente funzione C++:

void GetTime(const char* iv, struct timeval* tv) {
    uint32 val;
    memcpy(&val, iv, sizeof(val));
    tv->tv_sec = htonl(val);
    memcpy(&val, iv+sizeof(val), sizeof(val));
    tv->tv_usec = htonl(val)
}

Il timestamp può essere convertito in un formato leggibile utilizzando il seguente codice C++:

struct tm tm;
localtime_r(&tv->tv_sec, &tm);

printf("%04d-%02d-%02d|%02d:%02d:%02d.%06ld",
       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
       tm.tm_hour, tm.tm_min, tm.tm_sec,
       tv_.tv_usec);

Libreria Java

Anziché implementare gli algoritmi crittografici per codificare e decodificare il prezzo vincente, puoi utilizzare DoubleClickCrypto.java. Per ulteriori informazioni, consulta la sezione Crittografia.