Lorsque votre création remporte une enchère, Google peut vous indiquer le prix gagnant si elle inclut la macro ${AUCTION_PRICE}
.
Lorsque la macro est développée, elle renvoie le prix gagnant sous forme chiffrée. Il peut être inclus dans une création, par exemple, avec une demande de pixel invisible affichée dans l'annonce:
<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}
peut également être incluse dans l'URL VAST d'une création vidéo, mais pas dans l'URL d'impression dans le VAST:
https://example.com/vast/v?price=${AUCTION_PRICE}
Scénario
- Votre application d'enchères OpenRTB inclut la macro
${AUCTION_PRICE}
dans l'extrait HTML ou l'URL VAST qu'elle renvoie à Google. - Google remplace le prix gagnant par la macro dans un encodage Base64 non rempli, sécurisé pour le Web (RFC 3548).
- L'extrait transmet la confirmation au format que vous avez choisi. Par exemple, la confirmation peut être transmise dans l'URL d'une requête de pixel invisible affichée dans l'annonce.
- Sur le serveur, la base64 sécurisée pour les URL de votre application décode les informations sur le prix gagnant et déchiffre le résultat.
Dépendances
Vous aurez besoin d'une bibliothèque cryptographique compatible avec SHA-1 HMAC, telle qu'Openssl.
Exemple de code
Un exemple de code est fourni en Java et en C++, et peut être téléchargé à partir du projet privatedatacommunicationprotocol.
L'exemple de code Java utilise le décodeur base64 du projet Apache Commons. Vous n'avez pas besoin de télécharger le code Apache Commons, car l'implémentation de référence inclut la partie nécessaire et est donc autonome.
L'exemple de code C++ utilise la méthode BIO base64 OpenSSL. Il prend une chaîne encodée en base64 adaptée au Web (RFC 3548) et la décode. Normalement, les chaînes base64 sécurisées pour le Web remplacent le remplissage "=" par "." (notez que les guillemets sont ajoutés pour plus de clarté et ne sont pas inclus dans le protocole), mais le remplacement par macro ne remplit pas le prix chiffré. L'implémentation de référence ajoute un remplissage, car OpenSSL rencontre des problèmes avec les chaînes non remplies.
Encodage
Le chiffrement et le déchiffrement du prix gagnant nécessitent deux clés secrètes, mais partagées. Une clé d'intégrité et une clé de chiffrement, appelées respectivement i_key
et e_key
. Les deux clés sont fournies lors de la configuration du compte sous forme de chaînes base64 sécurisées pour le Web. Vous les trouverez sur la page "Acheteurs autorisés" sous Paramètres de l'enchérisseur > Paramètres RTB > Clés de chiffrement.
Exemples de clés d'intégrité et de chiffrement:
skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o= // Encryption key (e_key) arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo= // Integrity key (i_key)
Les clés doivent être décodées au format Web-safe, puis en base64 par votre application:
e_key = WebSafeBase64Decode('skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=') i_key = WebSafeBase64Decode('arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=')
Schéma de chiffrement
Le prix est chiffré à l'aide d'un schéma de chiffrement personnalisé conçu pour réduire les frais généraux de taille tout en garantissant une sécurité adéquate. Le schéma de chiffrement utilise un algorithme HMAC à clé pour générer un tampon secret basé sur l'ID d'événement d'impression unique.
Le prix chiffré a une longueur fixe de 28 octets. Il se compose d'un vecteur d'initialisation de 16 octets, de 8 octets de texte chiffré et d'une signature d'intégrité de 4 octets. Le prix chiffré est encodé en base64 sécurisé pour le Web, conformément à la norme RFC 3548, et les caractères de remplissage sont omis. Ainsi, le prix chiffré de 28 octets est encodé sous la forme d'une chaîne base64 de 38 caractères adaptée au Web, quel que soit le prix gagnant payé.
Exemples de prix chiffrés:
YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCce_6msaw // 100 CPI micros YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCAWJRxOgA // 1900 CPI micros YWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw // 2700 CPI micros
Le format chiffré est le suivant:
{initialization_vector (16 bytes)}{encrypted_price (8 bytes)} {integrity (4 bytes)}
Le prix est chiffré en tant que <price xor HMAC(encryption_key,
initialization_vector)>
. Le déchiffrement calcule donc HMAC(encryption_key,initialization_vector)
et xor avec le prix chiffré pour inverser le chiffrement. L'étape d'intégrité prend 4 octets de <HMAC(integrity_key, price||initialization_vector)>
, où ||
est la concaténation.
Entrées | |
---|---|
iv |
Vecteur d'initialisation (16 octets, propre à l'impression) |
e_key |
clé de chiffrement (32 octets, fournie lors de la configuration du compte) |
i_key |
clé d'intégrité (32 octets, fournie lors de la configuration du compte) |
price |
(8 octets, en micros de la devise du compte) |
Notation | |
hmac(k, d) |
HMAC SHA-1 des données d , à l'aide de la clé k |
a || b |
chaîne a concaténée avec la chaîne b |
Pseudo-code | |
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 ) |
Schéma de déchiffrement
Votre code de déchiffrement doit déchiffrer le prix à l'aide de la clé de chiffrement et vérifier les bits d'intégrité avec la clé d'intégrité. Les clés vous seront fournies lors de la configuration. Il n'existe aucune restriction concernant la manière dont vous structurez votre implémentation. Dans la plupart des cas, vous devriez pouvoir prendre l'exemple de code et l'adapter à vos besoins.
Entrées | |
---|---|
e_key |
clé de chiffrement, 32 octets : fournie lors de la configuration du compte |
i_key |
Clé d'intégrité, 32 octets : fournie lors de la configuration du compte |
final_message |
38 caractères encodés au format base64 adapté au Web |
Pseudo-code | |
// 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) |
Détecter les attaques par réponse obsolète
Pour détecter les attaques par réponse obsolète ou par rejeu, nous vous recommandons de filtrer les réponses dont le code temporel diffère considérablement de l'heure système, après avoir pris en compte les différences de fuseau horaire.
Le vecteur d'initialisation contient un code temporel dans les huit premiers octets. Il peut être lu par la fonction C++ suivante:
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) }
Le code temporel peut être converti en format lisible à l'aide du code C++ suivant:
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);
Bibliothèque Java
Au lieu d'implémenter les algorithmes de cryptographie pour encoder et décoder le prix gagnant, vous pouvez utiliser DoubleClickCrypto.java. Pour en savoir plus, consultez la section Cryptographie.