料金の確認の復号

クリエイティブがオークションで落札すると、クリエイティブに ${AUCTION_PRICE} マクロが含まれている場合、落札価格を通知できます。

マクロが展開されると、落札価格が暗号化された形式で返されます。広告の一部としてレンダリングされる非表示のピクセル リクエストなど、クリエイティブに含めることができます。

<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 URL にも含めることができますが、VAST のインプレッション URL には含めることはできません。

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

シナリオ

  1. OpenRTB 入札アプリケーションの HTML スニペットまたは Google に返す VAST URL に ${AUCTION_PRICE} マクロが含まれている。
  2. Google は、パディングなしのウェブセーフな Base64 エンコード(RFC 3548)で落札価格をマクロに置き換えます。
  3. 選択した形式で確認が渡されます。たとえば、広告の一部としてレンダリングされる非表示ピクセル リクエストの URL で確認が渡される場合があります。
  4. サーバーで、アプリケーションのウェブセーフ base64 が落札価格情報をデコードし、結果を復号します。

依存関係

Openssl など、SHA-1 HMAC をサポートする暗号ライブラリが必要です。

サンプルコード

サンプルコードは Java と C++ で提供されており、privatedatacommunicationprotocol プロジェクトからダウンロードできます。

  • Java サンプルコードでは、Apache Commons プロジェクトの base64 デコーダを使用しています。リファレンス実装には必要な部分が含まれているため、Apache Commons コードをダウンロードする必要はありません。

  • C++ サンプルコードでは、OpenSSL base64 BIO メソッドを使用しています。ウェブセーフな Base64 エンコード文字列(RFC 3548)を受け取り、デコードします。通常、ウェブセーフ Base64 文字列では、「="」の埋め込みが「"」に置き換えられます(引用符は読みやすくするために追加したもので、プロトコルには含まれません)。ただし、マクロ置換では暗号化された価格は埋め込まれません。OpenSSL はパディングなしの文字列で問題が発生するため、リファレンス実装ではパディングが追加されます。

エンコード

落札単価の暗号化と復号には、2 つの秘密鍵(共有鍵)が必要です。完全性鍵と暗号鍵(それぞれ i_keye_key)。どちらの鍵も、アカウントの設定時にウェブセーフの base64 文字列として提供されます。認定バイヤー ページの [入札者(ビッダー)の設定] > [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 演算を実行して暗号化を解除します。完全性ステージは 4 バイトの <HMAC(integrity_key, price||initialization_vector)> を取得します。ここで、|| は連結です。

入力
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 を使用できます。詳細については、暗号化をご覧ください。