Desencriptar confirmaciones de precio

Cuando tu creatividad gana una subasta, Google puede informarte cuál fue el precio ganador si el fragmento HTML o la URL de VAST que define la creatividad incluye la macro WINNING_PRICE. Google devuelve el precio ganador en formato encriptado. En los siguientes temas, se explica cómo tu aplicación puede desencriptar la información del precio ganador.

La macro WINNING_PRICE se puede incluir en una creatividad, por ejemplo, con una solicitud de píxel invisible renderizado como parte del anuncio:

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

La macro WINNING_PRICE también se puede incluir en la URL de VAST de una creatividad de video (pero no en la URL de impresión en VAST):

https://example.com/vast/v?price=%%WINNING_PRICE%%

Situación

  1. Tu aplicación incluye la macro WINNING_PRICE en el fragmento HTML o la URL de VAST que muestra a Google.
  2. Google sustituye el precio ganador de la macro en la codificación Base64 segura para la Web sin relleno (RFC 3548).
  3. El fragmento pasa la confirmación en el formato que elegiste. Por ejemplo, la confirmación se puede pasar en la URL de una solicitud de píxel invisible que se renderiza como parte del anuncio.
  4. En el servidor, la aplicación en Base64 segura para la Web decodifica la información del precio ganador y desencripta el resultado.

Dependencias

Necesitarás una biblioteca criptográfica que admita SHA-1 HMAC, como Openssl.

Código de muestra

El código de muestra se proporciona en Java y C++, y se puede descargar desde el proyecto privatedataCommunicationprotocol.

  • El código de muestra de Java usa el decodificador en base64 del proyecto Apache Commons. No es necesario que descargues el código de Apache Commons, ya que la implementación de referencia incluye la parte necesaria y, por lo tanto, es independiente.

  • El código de muestra de C++ usa el método BIO de base64 de OpenSSL. Toma una string codificada en base64 segura para la Web (RFC 3548) y la decodifica. Por lo general, las cadenas en base64 seguras para la Web reemplazan el relleno "=" por "." (ten en cuenta que las comillas se agregan para mayor claridad y no se incluyen en el protocolo), pero la sustitución de macro no rellena el precio encriptado. La implementación de referencia agrega relleno porque OpenSSL tiene problemas con las cadenas sin rellenar.

Codificación

Para ganar la encriptación y desencriptación de precios, se requieren dos claves secretas, pero compartidas. Una clave de integridad y una clave de encriptación, denominadas i_key y e_key, respectivamente Ambas claves se proporcionan en la configuración de la cuenta como strings de base64 seguras para la Web y se pueden encontrar en la página de Authorized Buyers, en Configuración del ofertante > Configuración de RTB > Claves de encriptación.

Ejemplos de claves de integridad y encriptación:

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

La aplicación debe decodificar las claves para la Web y, luego, en Base64:

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

Esquema de encriptación

El precio se encripta con un esquema de encriptación personalizado diseñado para minimizar el tamaño de la sobrecarga y, al mismo tiempo, garantizar una seguridad adecuada. El esquema de encriptación usa un algoritmo HMAC con clave para generar un área secreta basada en el ID de evento de impresión único.

El precio encriptado tiene una longitud fija de 28 bytes. Consta de un vector de inicialización de 16 bytes, 8 bytes de texto cifrado y una firma de integridad de 4 bytes. El precio encriptado está codificado en Base64 seguro para la Web, de acuerdo con RFC 3548, con caracteres de padding omitidos. Por lo tanto, el precio encriptado de 28 bytes se codifica como una cadena base 64 segura para la Web de 38 caracteres, independientemente del precio ganador que pague.

Ejemplos de precios encriptados:

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

El formato encriptado es el siguiente:

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

El precio está encriptado como <price xor HMAC(encryption_key, initialization_vector)>, por lo que la desencriptación calcula HMAC(encryption_key,initialization_vector) y xor con el precio encriptado para revertir la encriptación. La etapa de integridad toma 4 bytes de <HMAC(integrity_key, price||initialization_vector)>, en los que || es concatenación.

Entradas
iv vector de inicialización (16 bytes, único para la impresión)
e_key clave de encriptación (32 bytes, proporcionada durante la configuración de la cuenta)
i_key clave de integridad (32 bytes, proporcionada durante la configuración de la cuenta)
price (8 bytes en micros de la moneda de la cuenta)
Notation
hmac(k, d) HMAC SHA-1 de datos d, con la clave k
a || b cadena a concatenada con la cadena b
Pseudocódigo
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 )

Esquema de desencriptación

El código de desencriptación debe desencriptar el precio con la clave de encriptación y verificar los bits de integridad con la clave de integridad. Las claves se te proporcionarán durante la configuración. No hay restricciones en cuanto a los detalles de cómo estructuras tu implementación. En general, debes poder tomar el código de muestra y adaptarlo según tus necesidades.

Entradas
e_key clave de encriptación, 32 bytes (proporcionada durante la configuración de la cuenta)
i_key clave de integridad, 32 bytes (proporcionada durante la configuración de la cuenta)
final_message 38 caracteres codificado en base64 seguro para la Web
Pseudocódigo
// 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)

Detecta ataques de respuesta inactivos

Para detectar ataques de respuesta inactiva o de repetición, se recomienda filtrar las respuestas con una marca de tiempo que difiere significativamente de la hora del sistema, después de tener en cuenta las diferencias de las zonas horarias.

El vector de inicialización contiene una marca de tiempo en los primeros 8 bytes. Puede leerse con la siguiente función de 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)
}

La marca de tiempo se puede convertir en un formato legible por humanos a través del siguiente código de 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);

Biblioteca Java

En lugar de implementar los algoritmos criptográficos para codificar y decodificar el precio ganador, puedes usar DoubleClickCrypto.java. Para obtener más información, consulta Criptografía.