Sieci reklamowe korzystające z
tagów JavaScript do wypełniania reklam za pomocą Authorized Buyers mogą otrzymywać identyfikatory reklamodawców zarówno na urządzenia z Androidem, jak i z iOS.
Informacje są wysyłane za pomocą makra %%EXTRA_TAG_DATA%%
lub %%ADVERTISING_IDENTIFIER%%
w tagu JavaScript zarządzanym przez program Authorized Buyers. Pozostała część tej sekcji dotyczy wyodrębniania danych %%EXTRA_TAG_DATA%%
. W sekcji
Remarketing z wykorzystaniem identyfikatora IDFA lub identyfikatora wyświetlania reklam znajdziesz szczegółowe informacje o %%ADVERTISING_IDENTIFIER%%
zaszyfrowanym buforze protokołuMobileAdvertisingId
, który można w podobny sposób odszyfrować.
Oś czasu
- Sieć reklamowa aktualizuje tagi JavaScript w aplikacji za pomocą interfejsu Authorized Buyers, dodając makro
%%EXTRA_TAG_DATA%%
w sposób opisany poniżej. - W momencie wyświetlenia aplikacja wysyła żądanie reklamy do Authorized Buyers za pomocą pakietu SDK do reklam mobilnych Google, a jednocześnie bezpiecznie przekazuje identyfikator reklamodawcy.
- Aplikacja zwraca tag JavaScript, a makro
%%EXTRA_TAG_DATA%%
jest wypełnione zaszyfrowanym buforem protokołu sieci reklamowej zawierającym ten identyfikator. - Aplikacja uruchamia ten tag, wywołując sieć reklamową w przypadku zwycięskiej reklamy.
- Aby korzystać z tych informacji (zarabiać), sieć reklamowa musi przetworzyć bufor protokołu:
- Zdekoduj ciąg znaków w sieci z powrotem do ciągu bajtów przy użyciu WebSafeBase64.
- Odszyfruj go, korzystając ze schematu opisanego poniżej.
- Zdeserializuj proto i uzyskaj identyfikator reklamodawcy z ExtraTagData.advertising_id lub ExtraTagData.hashed_idfa.
Zależności
- Koder WebSafeBase64.
- Biblioteka kryptograficzna obsługująca protokół SHA-1 HMAC, np. Openssl.
- Kompilator bufora protokołu Google.
Dekoduj ciąg znaków dotyczący bezpieczeństwa w internecie
Informacje wysyłane za pomocą makra %%EXTRA_TAG_DATA%%
muszą być wysyłane za pomocą adresu URL, dlatego serwery Google kodują je w bezpiecznym formacie base64 (RFC 3548).
Dlatego przed próbą odszyfrowania musisz zdekodować znaki ASCII z powrotem do ciągu bajtów. Przykładowy kod w C++ poniżej jest oparty na projekcie BIO_f_base64()OpenSSL Project i jest częścią przykładowego kodu odszyfrowywania 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); }
Struktura zaszyfrowanego ciągu bajtów
Po zdekodowaniu znaków ASCII z powrotem do ciągu bajtów możesz go odszyfrować. Zaszyfrowany ciąg bajtów zawiera 3 sekcje:
initialization_vector
: 16 bajtów.ciphertext
: seria 20-bajtowych sekcji.integrity_signature
: 4 bajty.
{initialization_vector (16 bytes)}{ciphertext (20-byte sections)}{integrity_signature (4 bytes)}
Tablica bajtowa ciphertext
jest dzielona na wiele sekcji 20-bajtowych, z tym że ostatnia sekcja może zawierać od 1 do 20 bajtów. W przypadku każdej sekcji oryginalnej byte_array
odpowiedni 20-bajtowy element ciphertext
jest generowany jako:
<byte_array <xor> HMAC(encryption_key, initialization_vector || counter_bytes)>
gdzie ||
to konkatenacja.
Definicje
Zmienna | Szczegóły |
---|---|
initialization_vector |
16 bajtów – unikalny dla wyświetlenia. |
encryption_key |
32 bajty – podawane podczas zakładania konta. |
integrity_key |
32 bajty – podawane podczas zakładania konta. |
byte_array |
Zserializowany obiekt ExtraTagData w sekcjach 20-bajtowych. |
counter_bytes |
Wartość bajtów wskazująca numer porządkowy sekcji (patrz poniżej). |
final_message |
Łączna tablica bajtów wysłana przez makro %%EXTRA_TAG_DATA%% (bez kodowania WebSafeBase64). |
Operatory | Szczegóły |
---|---|
hmac(key, data) |
SHA-1 HMAC, używany do szyfrowania data , przy użyciu key . |
a || b |
ciąg znaków a połączony z ciągiem tekstowym b . |
Oblicz bajty licznika
counter_bytes
oznacza kolejność każdej 20-bajtowej sekcji ciphertext
. Pamiętaj, że ostatnia sekcja może zawierać od 1 do 20 bajtów. Aby wypełnić element counter_bytes
prawidłową wartością podczas uruchamiania funkcji hmac()
, policz sekcje 20-bajtowe (wraz z pozostałymi) i skorzystaj z tej tabeli referencyjnej:
Numer sekcji | Wartość: counter_bytes |
---|---|
0 | Brak |
1...256 | 1 bajt. Wartość rośnie sekwencyjnie od 0 do 255. |
257 ... 512 | 2 bajty. Wartość pierwszego bajtu to 0, a wartość drugiego bajta zwiększa się kolejno od 0 do 255. |
513 ... 768 | 3 bajty. Wartość z pierwszych 2 bajtów wynosi 0, a wartość ostatniego bajtu zwiększa się kolejno od 0 do 255. |
Schemat szyfrowania
Schemat szyfrowania jest oparty na tym samym schemacie co do odszyfrowywania sygnału kierowania hiperlokalnego.
Serializacja: instancja obiektu ExtraTagData zdefiniowana w buforze protokołu jest najpierw zserializowana przez
SerializeAsString()
do tablicy bajtów.Szyfrowanie: tablica bajtów jest następnie szyfrowana przy użyciu niestandardowego schematu szyfrowania, który miałby zminimalizować obciążenie związane z rozmiarem i zapewnić odpowiedni poziom bezpieczeństwa. Schemat szyfrowania korzysta z algorytmu HMAC z kluczem, aby generować tajną klawiaturę na podstawie parametru
initialization_vector
, która jest unikalna dla zdarzenia wyświetlenia.
Pseudokod szyfrowania
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
Schemat odszyfrowywania
Kod odszyfrowywania musi: 1) odszyfrować bufor protokołu przy użyciu klucza szyfrowania i 2) zweryfikować bity integralności za pomocą klucza integralności. Klucze zostaną Ci udostępnione podczas zakładania konta. Nie ma żadnych ograniczeń struktury implementacji. Najczęściej wystarczy wziąć przykładowy kod i dostosować go do swoich potrzeb.
- Wygeneruj pad:
HMAC(encryption_key, initialization_vector || counter_bytes)
- XOR: użyj tego wyniku i
<xor>
razem z tekstem szyfrowanym, aby odwrócić szyfrowanie. - Weryfikacja: podpis integralności przekazuje 4 bajty pliku
HMAC(integrity_key, byte_array || initialization_vector)
Pseudokod odszyfrowywania
// 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)
Przykładowy kod w C++
Tutaj znajdziesz funkcję klucza z naszego pełnego przykładowego kodu odszyfrowywania.
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'); } }
Pobieranie danych z bufora protokołów sieci reklamowej
Po zdekodowaniu i odszyfrowaniu danych przekazywanych w funkcji %%EXTRA_TAG_DATA%%
możesz dokonać deserializacji bufora protokołu i uzyskać identyfikator reklamodawcy na potrzeby kierowania.
Jeśli nie wiesz, czym są bufory protokołów, zacznij od naszej dokumentacji.
Definicja
Nasz bufor protokołu sieci reklamowej jest zdefiniowany następująco:
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; }
Musisz go poddać deserializacji za pomocą ParseFromString()
zgodnie z opisem w dokumentacji bufora protokołów C++.
Szczegółowe informacje o polach advertising_id
i iOS hashed_idfa
znajdziesz w artykułach Odszyfrowywanie identyfikatora wyświetlania reklam i Kierowanie na zasoby reklamowe w aplikacjach mobilnych za pomocą identyfikatora IDFA.
Biblioteka Java
Zamiast implementować algorytmy kryptograficzne do kodowania i dekodowania identyfikatorów reklamodawcy w sieciach reklamowych, możesz użyć DoubleClickCrypto.java. Więcej informacji znajdziesz w artykule na temat kryptografii.