JavaScript タグを使用して認定バイヤーから広告を配信する広告ネットワークでは、Android デバイスと iOS デバイスの両方で広告主 ID を受け取ることができます。この情報は、認定バイヤーが管理する JavaScript タグの %%EXTRA_TAG_DATA%%
または %%ADVERTISING_IDENTIFIER%%
マクロを介して送信されます。このセクションの残りの部分では %%EXTRA_TAG_DATA%%
を抽出することに重点を置いていますが、同じように復号できる %%ADVERTISING_IDENTIFIER%%
暗号化されたプロトコル バッファ MobileAdvertisingId
について詳しくは、
IDFA または広告 ID によるリマーケティングをご覧ください。
タイムライン
- 広告ネットワークでは、認定バイヤーの管理画面から JavaScript アプリ内タグを更新し、
%%EXTRA_TAG_DATA%%
マクロを追加します(下記の説明を参照)。 - 配信時、アプリは Google Mobile Ads SDK を介して認定バイヤーに広告をリクエストし、同時に広告主の識別子は安全に渡します。
- アプリは JavaScript タグを受信します。
%%EXTRA_TAG_DATA%%
マクロには、その識別子を含む暗号化された広告ネットワーク プロトコル バッファが入力されています。 - アプリはこのタグを実行し、落札広告の広告ネットワークを呼び出します。
- この情報を使用(収益化)するには、広告ネットワークがプロトコル バッファを処理する必要があります。
- WebSafeBase64 を使用して、ウェブセーフの文字列をバイト文字列にデコードします。
- 以下に概説するスキームを使用して復号します。
- プロトコルを逆シリアル化し、ExtraTagData.advertising_id または ExtraTagData.hashed_idfa から広告主 ID を取得します。
依存関係
- WebSafeBase64 エンコーダ
- SHA-1 HMAC をサポートする暗号ライブラリ(Openssl など)。
- Google のプロトコル バッファ コンパイラ。
ウェブセーフ文字列をデコードする
%%EXTRA_TAG_DATA%%
マクロで送信される情報は URL 経由で送信する必要があるため、Google のサーバーはこれをウェブセーフな Base64(RFC 3548)でエンコードします。
したがって、復号を行う前に、ASCII 文字をバイト文字列にデコードする必要があります。以下の C++ コードの例は、OpenSSL プロジェクトの BIO_f_base64() に基づくものであり、Google の復号コード例の一部です。
string AddPadding(const string& b64_string) { if (b64_string.size() % 4 == 3) { return b64_string + "="; } else if (b64_string.size() % 4 == 2) { return b64_string + "=="; } return b64_string; } // Adapted from http://www.openssl.org/docs/man1.1.0/crypto/BIO_f_base64.html // Takes a web safe base64 encoded string (RFC 3548) and decodes it. // Normally, web safe base64 strings have padding '=' replaced with '.', // but we will not pad the ciphertext. We add padding here because // openssl has trouble with unpadded strings. string B64Decode(const string& encoded) { string padded = AddPadding(encoded); // convert from web safe -> normal base64. int32 index = -1; while ((index = padded.find_first_of('-', index + 1)) != string::npos) { padded[index] = '+'; } index = -1; while ((index = padded.find_first_of('_', index + 1)) != string::npos) { padded[index] = '/'; } // base64 decode using openssl library. const int32 kOutputBufferSize = 256; char output[kOutputBufferSize]; BIO* b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); BIO* bio = BIO_new_mem_buf(const_cast(padded.data()), padded.length()); bio = BIO_push(b64, bio); int32 out_length = BIO_read(bio, output, kOutputBufferSize); BIO_free_all(bio); return string(output, out_length); }
暗号化されたバイト文字列の構造
ASCII 文字をバイト文字列にデコードしたら、復号できます。暗号化されたバイト文字列には、次の 3 つのセクションが含まれます。
initialization_vector
: 16 バイト。ciphertext
: 一連の 20 バイトのセクション。integrity_signature
: 4 バイト。
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}
ciphertext
バイト配列は、複数の 20 バイトのセクションに分割されます。ただし、最後のセクションには 1 ~ 20 バイトの範囲が含まれます。元の byte_array
の各セクションに対して、対応する 20 バイトの ciphertext
が次のように生成されます。
<byte_array <xor> HMAC(encryption_key, initialization_vector || counter_bytes)>
ここで、||
は連結です。
定義
変数 | 詳細 |
---|---|
initialization_vector |
16 バイト - インプレッションに固有の値。 |
encryption_key |
32 バイト - アカウント設定時に提供されます。 |
integrity_key |
32 バイト - アカウント設定時に提供されます。 |
byte_array |
シリアル化された ExtraTagData オブジェクト(20 バイトのセクション)。 |
counter_bytes |
セクションの序数を示すバイト値。下記をご覧ください。 |
final_message |
%%EXTRA_TAG_DATA%% マクロによって送信されたバイト配列の合計(WebSafeBase64 エンコードを差し引いた値)。 |
演算子 | 詳細 |
---|---|
hmac(key, data) |
SHA-1 HMAC。key を使用して data を暗号化します。 |
a || b |
文字列 a が文字列 b と連結されます。 |
count_bytes を計算する
counter_bytes
は、ciphertext
の各 20 バイト セクションの順序をマークします。最後のセクションには、1 ~ 20 バイトが含まれている可能性があります。hmac()
関数の実行時に counter_bytes
に正しい値を入力するには、20 バイトのセクション(余りを含む)を数え、次の参照表を使用します。
セクション番号 | counter_bytes 値 |
---|---|
撮影していない | なし |
1 ... 256 | 1 バイトです。値は 0 ~ 255 で順次増分します。 |
257 ~ 512 | 2 バイトです。最初のバイトの値は 0、2 番目のバイトの値は 0 から 255 まで順次増加します。 |
513 ~ 768 | 3 バイトです。最初の 2 バイトの値は 0 で、最後のバイトの値は 0 から 255 まで順次増加します。 |
暗号化スキーム
この暗号化スキームは、ハイパーローカル ターゲティング信号の復号に使用されるものと同じスキームに基づいています。
シリアル化: プロトコル バッファで定義されている ExtraTagData オブジェクトのインスタンスは、最初に
SerializeAsString()
によってバイト配列にシリアル化されます。暗号化: 次に、バイト配列は、適切なセキュリティを確保しながらサイズのオーバーヘッドを最小限に抑えるよう設計されたカスタム暗号化スキームを使用して暗号化されます。この暗号化スキームでは、鍵付き HMAC アルゴリズムを使用して、インプレッション イベントに固有の
initialization_vector
に基づいてシークレット パッドを生成します。
暗号化疑似コード
byte_array = SerializeAsString(ExtraTagData object) pad = hmac(encryption_key, initialization_vector || counter_bytes ) // for each 20-byte section of byte_array ciphertext = pad <xor> byte_array // for each 20-byte section of byte_array integrity_signature = hmac(integrity_key, byte_array || initialization_vector) // first 4 bytes final_message = initialization_vector || ciphertext || integrity_signature
復号スキーム
復号コードは、1)暗号鍵を使用してプロトコル バッファを復号し、2)整合性キーで整合性ビットを検証する必要があります。鍵は、アカウント設定時に提供されます。実装の構成方法に制限はありません。ほとんどの場合、サンプルコードは必要に応じて調整できます。
- パッドを生成:
HMAC(encryption_key, initialization_vector || counter_bytes)
- XOR: この結果と
<xor>
を暗号テキストに置き換えて、暗号化を元に戻します。 - 検証: 完全性署名は 4 バイトの
HMAC(integrity_key, byte_array || initialization_vector)
を渡します。
復号疑似コード
// split up according to length rules (initialization_vector, ciphertext, integrity_signature) = final_message // for each 20-byte section of ciphertext pad = hmac(encryption_key, initialization_vector || counter_bytes) // for each 20-byte section of ciphertext byte_array = ciphertext <xor> pad confirmation_signature = hmac(integrity_key, byte_array || initialization_vector) success = (confirmation_signature == integrity_signature)
サンプル C++ コード
ここには、完全な復号のサンプルコードの鍵関数が含まれています。
bool DecryptByteArray( const string& ciphertext, const string& encryption_key, const string& integrity_key, string* cleartext) { // Step 1. find the length of initialization vector and clear text. const int cleartext_length = ciphertext.size() - kInitializationVectorSize - kSignatureSize; if (cleartext_length < 0) { // The length cannot be correct. return false; } string iv(ciphertext, 0, kInitializationVectorSize); // Step 2. recover clear text cleartext->resize(cleartext_length, '\0'); const char* ciphertext_begin = string_as_array(ciphertext) + iv.size(); const char* const ciphertext_end = ciphertext_begin + cleartext->size(); string::iterator cleartext_begin = cleartext->begin(); bool add_iv_counter_byte = true; while (ciphertext_begin < ciphertext_end) { uint32 pad_size = kHashOutputSize; uchar encryption_pad[kHashOutputSize]; if (!HMAC(EVP_sha1(), string_as_array(encryption_key), encryption_key.length(), (uchar*)string_as_array(iv), iv.size(), encryption_pad, &pad_size)) { printf("Error: encryption HMAC failed.\n"); return false; } for (int i = 0; i < kBlockSize && ciphertext_begin < ciphertext_end; ++i, ++cleartext_begin, ++ciphertext_begin) { *cleartext_begin = *ciphertext_begin ^ encryption_pad[i]; } if (!add_iv_counter_byte) { char& last_byte = *iv.rbegin(); ++last_byte; if (last_byte == '\0') { add_iv_counter_byte = true; } } if (add_iv_counter_byte) { add_iv_counter_byte = false; iv.push_back('\0'); } }
広告ネットワーク プロトコル バッファからデータを取得する
%%EXTRA_TAG_DATA%%
で渡されたデータをデコードして復号したら、プロトコル バッファのシリアル化を解除して、ターゲティング用の広告主 ID を取得できます。
プロトコル バッファに不慣れな場合は、まずドキュメントをご覧ください。
定義
広告ネットワーク プロトコル バッファは次のように定義されます。
message ExtraTagData { // advertising_id can be Apple's identifier for advertising (IDFA) // or Android's advertising identifier. When the advertising_id is an IDFA, // it is the plaintext returned by iOS's [ASIdentifierManager // advertisingIdentifier]. For hashed_idfa, the plaintext is the MD5 hash of // the IDFA. Only one of the two fields will be available, depending on the // version of the SDK making the request. Later SDKs provide unhashed values. optional bytes advertising_id = 1; optional bytes hashed_idfa = 2; }
C++ プロトコル バッファのドキュメントで説明されているように、ParseFromString()
を使用してシリアル化を解除する必要があります。
Android の advertising_id
および iOS の hashed_idfa
フィールドについて詳しくは、広告 ID を復号すると IDFA を使用してモバイルアプリ広告枠をターゲットに設定するをご覧ください。
Java ライブラリ
広告ネットワーク用の広告主 ID をエンコードおよびデコードする暗号アルゴリズムを実装する代わりに、 DoubleClickCrypto.java を使用できます。詳細については、暗号化をご覧ください。