解密價格確認

如果廣告素材包含 ${AUCTION_PRICE} 巨集,Google 會在廣告素材得標時通知您得標價格。

展開巨集時,巨集會以加密形式傳回勝出價格。例如,您可以在廣告素材中加入這項資訊,並在廣告中顯示不可見的像素要求:

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

${AUCTION_PRICE} 巨集也可以納入影片廣告素材的 VAST 網址,但不能納入 VAST 中的曝光網址:

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

情境

  1. 您的 OpenRTB 出價應用程式在傳回 Google 的 HTML 程式碼片段或 VAST 網址中,包含 ${AUCTION_PRICE} 巨集。
  2. Google 會以未填充的網路安全 Base64 編碼 (RFC 3548) 取代巨集的得標價格。
  3. 程式碼片段會以您選擇的格式傳遞確認訊息。舉例來說,確認訊息可能會傳遞至廣告中顯示的隱藏像素要求網址。
  4. 在伺服器上,應用程式會使用網頁安全的 base64 解碼功能,解碼得標價格資訊並解密結果。

依附元件

您需要支援 SHA-1 HMAC 的加密編譯程式庫,例如 Openssl。

程式碼範例

範例程式碼以 Java 和 C++ 提供,可從 privatedatacommunicationprotocol 專案下載。

  • Java 程式碼範例會使用 Apache 共用程式專案中的 base64 解碼器。您不需要下載 Apache commons 程式碼,因為參考實作項目包含必要的部分,因此是自給自足的。

  • C++ 範例程式碼會使用 OpenSSL base64 BIO 方法。這個函式會採用具網路安全性的 Base64 編碼字串 (RFC 3548) 並進行解碼。一般來說,網頁安全的 base64 字串會將「="」填充字元替換成「.」(請注意,引號是為了閱讀清楚而加入,並未包含在通訊協定中),但巨集替換作業不會填入加密價格。參考實作會加入填充字元,因為 OpenSSL 無法處理未填充的字串。

編碼

得標價格加密和解密作業需要兩個共用密鑰。完整性金鑰和加密金鑰,分別稱為 i_keye_key。這兩組金鑰會在帳戶設定時以網頁安全的 base64 字串形式提供,並可在 Authorized Buyers 頁面的「出價工具設定 > RTB 設定 > 加密金鑰下方找到。

完整性和加密金鑰範例:

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

應用程式應先對金鑰進行網路安全解碼,再進行 Base64 解碼:

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

加密配置

價格會使用自訂加密方案加密,這個方案旨在盡可能減少大小開銷,同時確保足夠的安全性。加密方案會使用金鑰 HMAC 演算法,根據不重複的曝光事件 ID 產生密鑰填充資料。

加密價格的長度固定為 28 位元組。這項資料包含 16 位元組的初始化向量、8 位元組的密文,以及 4 位元組的完整性簽章。根據 RFC 3548,加密的價格採用網路安全 Base64 編碼,並省略填充字元。因此,無論支付的得標價為何,28 個位元組的加密價格都會編碼為 38 個字元的網路安全 Base64 字串。

加密價格範例:

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

加密格式如下:

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

價格會以 <price xor HMAC(encryption_key, initialization_vector)> 加密,因此解密程序會計算 HMAC(encryption_key,initialization_vector),並與經過加密的價格進行 XOR 運算,以便解除加密。完整性階段會使用 <HMAC(integrity_key, price||initialization_vector)> 的 4 個位元組,其中 || 是連結。

輸入
iv 初始化向量 (16 個位元組 - 曝光次數)
e_key 加密金鑰 (32 個位元組 - 在帳戶設定時提供)
i_key 完整性金鑰 (32 個位元組 - 在帳戶設定時提供)
price (8 個位元組 - 以帳戶貨幣的百萬分之一為單位)
Notation
hmac(k, d) 使用金鑰 k 計算資料 d 的 SHA-1 HMAC
a || b 字串 a 與字串 b 串連
虛擬程式碼
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 )

解密配置

解密程式碼必須使用加密金鑰解密價格,並使用完整性金鑰驗證完整性位元。系統會在設定期間提供這些金鑰。實作結構的詳細資料沒有任何限制。在大多數情況下,您應該可以使用程式碼範例,並根據需求加以調整。

輸入
e_key 加密金鑰 (32 個位元組) - 在帳戶設定時提供
i_key 完整性金鑰 (32 個位元組) - 在帳戶設定時提供
final_message 38 個字元的網路安全 Base64 編碼
虛擬程式碼
// 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)

偵測過時回應攻擊

如要偵測過時回應或重播攻擊,建議您在考量時區差異後,篩除時間戳記與系統時間有顯著差異的回應。

初始化向量會在前 8 個位元組中包含時間戳記。以下 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)
}

您可以使用下列 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);

Java 程式庫

您可以使用 DoubleClickCrypto.java,而非實作加密演算法來編碼及解碼得標價格。詳情請參閱「密碼學」。