Odszyfrowywanie potwierdzeń cen

Gdy kreacja wygra aukcję, Google może poinformować Cię, jaka była zwycięska cena, jeśli fragment kodu HTML lub adres URL VAST, który ją definiuje, zawiera makro WINNING_PRICE. Google zwraca zwycięską cenę w postaci zaszyfrowanej. W sekcjach poniżej wyjaśniono, jak aplikacja może odszyfrować informacje o zwycięskiej cenie.

Makro WINNING_PRICE można umieścić w kreacji, np. w przypadku renderowania niewidocznego żądania piksela:

<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>

Makro WINNING_PRICE można też umieścić w adresie URL VAST kreacji wideo (ale nie w adresie URL wyświetlenia w VAST):

https://example.com/vast/v?price=%%WINNING_PRICE%%

Scenariusz

  1. Aplikacja umieszcza makro WINNING_PRICE we fragmencie kodu HTML lub adresie URL VAST zwracanym do Google.
  2. Google zastępuje zwycięską cenę makro za pomocą niedopełnionego kodowania base64 i bezpiecznego dla stron internetowych (RFC 3548).
  3. Fragment kodu przekazuje potwierdzenie w wybranym formacie. Potwierdzenie może być na przykład przekazywane w adresie URL niewidocznego żądania piksela renderowanego w ramach reklamy.
  4. Na serwerze bezpieczna w internecie aplikacja base64 dekoduje informacje o zwycięskiej cenie i odszyfrowuje wynik.

Zależności

Potrzebujesz biblioteki kryptograficznej obsługującej protokół HMAC SHA-1, np. Openssl.

Przykładowy kod

Przykładowy kod jest udostępniony w języku Java i C++ i można go pobrać z projektu privatedatacommunicationprotocol.

  • Przykładowy kod w Javie korzysta z dekodera base64 z projektu Apache commons. Nie musisz pobierać kodu Apache commons, ponieważ implementacja referencyjna zawiera niezbędną część i jest samodzielna.

  • W przykładowym kodzie C++ użyto metody OpenSSL base64 BIO. Wykorzystuje do tego bezpieczny w internecie ciąg zakodowany w standardzie base64 (RFC 3548) i dekoduje go. Normalnie ciągi base64 przeznaczone do użytku w internecie zastępują dopełnienie „=” znakiem „.” (pamiętaj, że dla ułatwienia odczytu znaki cudzysłowu są dodawane, ale nie są uwzględnione w protokole), ale zastępowanie makrem nie zakłóca zaszyfrowanej ceny. Implementacja referencyjna dodaje dopełnienie, ponieważ OpenSSL ma problemy z ciągami znaków bez dopełnienia.

Kodowanie

Szyfrowanie i odszyfrowywanie zwycięskiej ceny wymaga 2 tajnych, ale wspólnych kluczy. Klucz integralności i klucz szyfrowania – odpowiednio i_key i e_key. Oba klucze są udostępniane podczas konfiguracji konta jako bezpieczne w internecie ciągi znaków base64. Można je znaleźć na stronie Authorized Buyers w sekcji Ustawienia systemu licytującego > Ustawienia RTB > Klucze szyfrowania.

Przykładowe klucze integralności i szyfrowania:

skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=  // Encryption key (e_key)
arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=  // Integrity key (i_key)

Klucze powinny być dekodowane do bezpiecznego internetu, a następnie dekodowane w formacie base64 przez aplikację:

e_key = WebSafeBase64Decode('skU7Ax_NL5pPAFyKdkfZjZz2-VhIN8bjj1rVFOaJ_5o=')
i_key = WebSafeBase64Decode('arO23ykdNqUQ5LEoQ0FVmPkBd7xB5CO89PDZlSjpFxo=')

Schemat szyfrowania

Cena jest szyfrowana z użyciem niestandardowego schematu szyfrowania, który ma na celu ograniczenie obciążenia i zapewnienie odpowiednich bezpieczeństwa. Schemat szyfrowania wykorzystuje algorytm HMAC z kluczem, aby wygenerować tajny klucz na podstawie unikalnego identyfikatora zdarzenia wyświetlenia.

Zaszyfrowana cena ma stałą długość 28 bajtów. Składa się z 16-bajtowego wektora inicjującego, 8 bajtów tekstu szyfrowanego i 4-bajtowej sygnatury integralności. Zaszyfrowana cena jest zakodowana w internecie zgodnie z RFC 3548 w standardzie base64 z pominięciem znaków dopełniających. Tak więc 28-bajtowa szyfrowana cena jest kodowana jako 38-znakowy ciąg tekstowy w formacie base-64 przeznaczonym do bezpiecznego przesyłania w internecie, niezależnie od zapłaconej ceny.

Przykładowe zaszyfrowane ceny:

YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCce_6msaw  // 100 CPI micros
YWJjMTIzZGVmNDU2Z2hpN7fhCuPemCAWJRxOgA  // 1900 CPI micros
YWJjMTIzZGVmNDU2Z2hpN7fhCuPemC32prpWWw  // 2700 CPI micros

Zaszyfrowany format:

{initialization_vector (16 bytes)}{encrypted_price (8 bytes)}
{integrity (4 bytes)}

Cena jest zaszyfrowana jako <price xor HMAC(encryption_key, initialization_vector)>, więc podczas odszyfrowywania oblicza się wartość HMAC(encryption_key,initialization_vector), a xor z zaszyfrowaną ceną, aby cofnąć szyfrowanie. Na etapie integralności są 4 bajty <HMAC(integrity_key, price||initialization_vector)>, gdzie || to konkatenacja.

Dane wejściowe
iv wektor inicjujący (16 bajtów – unikalny dla wyświetlenia)
e_key klucz szyfrowania (32 bajty – podany podczas konfigurowania konta)
i_key klucz integralności (32 bajty – podany podczas konfigurowania konta)
price (8 bajtów w milionowych częściach waluty konta)
Zapis
hmac(k, d) HMAC SHA-1 danych d przy użyciu klucza k
a || b ciąg tekstowy a połączony z ciągiem znaków b
Pseudokod
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 )

Schemat odszyfrowywania

Twój kod odszyfrowywania musi odszyfrować cenę za pomocą klucza szyfrowania i zweryfikować bity integralności kluczem integralności. Klucze zostaną Ci udostępnione podczas konfiguracji. Nie ma żadnych ograniczeń dotyczących struktury implementacji. Najczęstszą częścią tego rozwiązania jest wykorzystanie przykładowego kodu i dostosowanie go do swoich potrzeb.

Dane wejściowe
e_key klucz szyfrowania o długości 32 bajtów – dostarczony podczas konfigurowania konta
i_key klucz integralności, 32 bajty – dostarczony podczas konfigurowania konta
final_message 38 znaków, przeznaczone do bezpieczeństwa w internecie, zakodowane w base64
Pseudokod
// 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)

Wykrywanie ataków nieaktualnych odpowiedzi

Aby wykrywać nieaktualne odpowiedzi lub ponowne próby ataków, zalecamy odfiltrowanie odpowiedzi z sygnaturą czasową, która znacznie różni się od czasu systemowego, uwzględniając różnice między strefami czasowymi.

Wektor inicjujący zawiera sygnaturę czasową w pierwszych 8 bajtach. Można go odczytać za pomocą tej funkcji 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)
}

Sygnaturę czasową można przekształcić do postaci czytelnej dla człowieka za pomocą tego kodu w języku 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);

Biblioteka Java

Zamiast implementować algorytmy kryptograficzne w celu kodowania i dekodowania zwycięskiej ceny, możesz użyć pliku DoubleClickCrypto.java. Więcej informacji znajdziesz w sekcji Kryptografia.