如果廣告素材包含 ${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}
情境
- 您的 OpenRTB 出價應用程式在傳回 Google 的 HTML 程式碼片段或 VAST 網址中,包含
${AUCTION_PRICE}
巨集。 - Google 會以未填充的網路安全 Base64 編碼 (RFC 3548) 取代巨集的得標價格。
- 程式碼片段會以您選擇的格式傳遞確認訊息。舉例來說,確認訊息可能會傳遞至廣告中顯示的隱藏像素要求網址。
- 在伺服器上,應用程式會使用網頁安全的 base64 解碼功能,解碼得標價格資訊並解密結果。
依附元件
您需要支援 SHA-1 HMAC 的加密編譯程式庫,例如 Openssl。
程式碼範例
範例程式碼以 Java 和 C++ 提供,可從 privatedatacommunicationprotocol 專案下載。
Java 程式碼範例會使用 Apache 共用程式專案中的 base64 解碼器。您不需要下載 Apache commons 程式碼,因為參考實作項目包含必要的部分,因此是自給自足的。
C++ 範例程式碼會使用 OpenSSL base64 BIO 方法。這個函式會採用具網路安全性的 Base64 編碼字串 (RFC 3548) 並進行解碼。一般來說,網頁安全的 base64 字串會將「="」填充字元替換成「.」(請注意,引號是為了閱讀清楚而加入,並未包含在通訊協定中),但巨集替換作業不會填入加密價格。參考實作會加入填充字元,因為 OpenSSL 無法處理未填充的字串。
編碼
得標價格加密和解密作業需要兩個共用密鑰。完整性金鑰和加密金鑰,分別稱為 i_key
和 e_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,而非實作加密演算法來編碼及解碼得標價格。詳情請參閱「密碼學」。