當您的廣告素材贏得競價時,如果定義廣告素材的 HTML 程式碼片段或 VAST 網址包含 WINNING_PRICE
巨集,Google 將通知您得標的價格。Google 會以加密形式傳回勝出的價格。下列主題會說明應用程式如何解密勝出的價格資訊。
您可以在廣告素材中加入 WINNING_PRICE
巨集,例如,隱藏像素請求會當做廣告的一部分顯示:
<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>
影片廣告素材的 VAST 網址也可包含 WINNING_PRICE
巨集 (但不包含在 VAST 中的曝光網址):
https://example.com/vast/v?price=%%WINNING_PRICE%%
情境
- 您的應用程式會在傳送給 Google 的 HTML 程式碼片段或 VAST 網址中加入
WINNING_PRICE
巨集。 - Google 使用無填湊的網路安全 base64 編碼方式 (RFC 3548),將巨集替換成得標的價格。
- 程式碼片段會以您選擇的格式傳送確認訊息。舉例來說,確認可以傳遞做為廣告內隱藏像素請求的網址。
- 在伺服器上,應用程式安全的 base64 會將得標價格資訊解碼,並解密結果。
依附元件
您需要支援 SHA-1 HMAC 的密碼編譯程式庫,例如 Openssl。
程式碼範例
程式碼範例提供 Java 和 C++,可從 privatedatacommunicationprotocol 專案下載。
Java 程式碼範例會使用 Apache Commons 專案的 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 個字元的網路安全 Base-64 字串。
加密價格範例:
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) |
資料 d 的 SHA-1 HMAC,使用金鑰 k |
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 |
使用網路安全 Base64 編碼的 38 個字元 |
虛擬程式碼 | |
// 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 來編碼並解碼勝出的價格,不必實作加密演算法。詳情請參閱「密碼編譯」。