Рекламные сети, использующие теги JavaScript для заполнения объявлений через Авторизованных покупателей, имеют право получать идентификаторы рекламодателя как для устройств Android, так и для iOS. Информация отправляется через макрос %%EXTRA_TAG_DATA%%
или %%ADVERTISING_IDENTIFIER%%
в теге JavaScript, управляемом Авторизованными покупателями. Остальная часть этого раздела посвящена извлечению %%EXTRA_TAG_DATA%%
, но подробности о зашифрованном прото-буфере %%ADVERTISING_IDENTIFIER%%
MobileAdvertisingId
, который можно расшифровать аналогичным образом, см. в разделе Ремаркетинг с IDFA или рекламным идентификатором.
Хронология
- Рекламная сеть обновляет теги JavaScript в приложениях через пользовательский интерфейс Авторизованных покупателей , добавляя макрос
%%EXTRA_TAG_DATA%%
, как описано ниже. - Во время показа приложение запрашивает рекламу у Авторизованных покупателей через Google Mobile Ads SDK , безопасно передавая идентификатор рекламодателя.
- Приложение получает обратно тег JavaScript с макросом
%%EXTRA_TAG_DATA%%
, заполненным зашифрованным буфером протокола рекламной сети, содержащим этот идентификатор. - Приложение запускает этот тег, вызывая рекламную сеть для поиска победившего объявления.
- Чтобы использовать (монетизировать) эту информацию, рекламная сеть должна обработать буфер протокола:
- Декодируйте строку веб-безопасности обратно в байтовую строку с помощью WebSafeBase64.
- Расшифруйте его, используя схему, изложенную ниже.
- Десериализуйте прототип и получите идентификатор рекламодателя из ExtraTagData.advertising_id или ExtraTagData.hashed_idfa.
Зависимости
- Кодировщик WebSafeBase64 .
- Криптобиблиотека, поддерживающая SHA-1 HMAC, например Openssl .
- Компилятор буфера протокола Google.
Декодировать строку веб-безопасности
Поскольку информация, отправляемая через макрос %%EXTRA_TAG_DATA%%
должна отправляться через URL-адрес, серверы Google кодируют ее с помощью веб-безопасного base64 ( RFC 3548 ).
Поэтому, прежде чем пытаться расшифровать, вы должны декодировать символы ASCII обратно в байтовую строку. Приведенный ниже пример кода C++ основан на BIO_f_base64() проекта OpenSSL и является частью примера кода расшифровки 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<char*>(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 . |
Вычислить счетчик_байтов
counter_bytes
отмечает порядок каждой 20-байтовой секции ciphertext
. Обратите внимание, что последняя секция может содержать от 1 до 20 байт включительно. Чтобы заполнить counter_bytes
правильным значением при запуске функции hmac()
, подсчитайте 20-байтовые секции (включая остаток) и используйте следующую справочную таблицу:
Номер раздела | значение counter_bytes |
---|---|
0 | Никто |
1 … 256 | 1 байт. Значение последовательно увеличивается от 0 до 255. |
257…512 | 2 байта. Значение первого байта равно 0, значение второго байта последовательно увеличивается от 0 до 255. |
513…768 | 3 байта. Значение первых двух байтов равно 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%%
, вы готовы десериализовать буфер протокола и получить идентификатор рекламодателя для таргетинга.
Если вы не знакомы с буферами протоколов, начните с нашей документации .
Определение
Наш буфер протокола рекламной сети определяется следующим образом:
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; }
Вам нужно будет десериализовать его с помощью ParseFromString()
, как описано в документации по буферу протокола C++ .
Подробные сведения о полях advertising_id
и iOS hashed_idfa
см. в разделах «Расшифровка рекламного идентификатора» и «Таргетинг ресурсов мобильных приложений с помощью IDFA» .
Java-библиотека
Вместо реализации криптоалгоритмов для кодирования и декодирования идентификаторов рекламодателей для рекламных сетей вы можете использовать DoubleClickCrypto.java . Дополнительные сведения см. в разделе Криптография .